function [s, cfg] = ft_statfun_depsamplesFmultivariate(cfg, dat, design)

% FT_STATFUN_DEPSAMPLESFMULTIVARIATE calculates the MANOVA dependent samples
% F-statistic on the biological data in dat (the dependent variable), using the
% information on the independent variable (ivar) in design.
%
% Use this function by calling one of the high-level statistics functions as
%   [stat] = ft_timelockstatistics(cfg, timelock1, timelock2, ...)
%   [stat] = ft_freqstatistics(cfg, freq1, freq2, ...)
%   [stat] = ft_sourcestatistics(cfg, source1, source2, ...)
% with the following configuration option
%   cfg.statistic = 'ft_statfun_depsamplesFmultivariate'
%
% Configuration options
%   cfg.contrastcoefs  = matrix of contrast coefficients determining the
%                        effect being tested. The number of columns of this
%                        matrix has to be equal to the number of conditions.
%                        The default is a matrix that specifies the
%                        main effect of the independent variable. This matrix
%                        has size [(ncond-1),ncond].
%   cfg.computestat    = 'yes' or 'no', calculate the statistic (default='yes')
%   cfg.computecritval = 'yes' or 'no', calculate the critical values of the test statistics (default='no')
%   cfg.computeprob    = 'yes' or 'no', calculate the p-values (default='no')
%
% The following options are relevant if cfg.computecritval='yes' and/or
% cfg.computeprob='yes'.
%   cfg.alpha = critical alpha-level of the statistical test (default=0.05)
%   cfg.tail  = -1, 0, or 1, left, two-sided, or right (default=1)
%               cfg.tail in combination with cfg.computecritval='yes'
%               determines whether the critical value is computed at
%               quantile cfg.alpha (with cfg.tail=-1), at quantiles
%               cfg.alpha/2 and (1-cfg.alpha/2) (with cfg.tail=0), or at
%               quantile (1-cfg.alpha) (with cfg.tail=1).
%
% Design specification
%   cfg.ivar  = row number of the design that contains the labels of the conditions that must be
%               compared (default=1). The labels range from 1 to the number of conditions.
%   cfg.uvar  = row number of design that contains the labels of the units-of-observation (subjects or trials)
%               (default=2). The labels are assumed to be integers ranging from 1 to
%               the number of units-of-observation.
%
% See also FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS

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

% set defaults
if ~isfield(cfg, 'computestat'),       cfg.computestat='yes';     end
if ~isfield(cfg, 'computecritval'),    cfg.computecritval='no';   end
if ~isfield(cfg, 'computeprob'),       cfg.computeprob='no';      end
if ~isfield(cfg, 'alpha'),             cfg.alpha=0.05;            end
if ~isfield(cfg, 'tail'),              cfg.tail=1;                end

nconds=length(unique(design(cfg.ivar,:)));
if ~isfield(cfg,'contrastcoefs')
    % specify the default contrast coefficient matrix.
    ncontrasts = nconds-1;
    cfg.contrastcoefs = zeros(ncontrasts,nconds);
    cfg.contrastcoefs(:,1) = 1;
    for contrastindx=1:ncontrasts
        cfg.contrastcoefs(contrastindx,contrastindx+1)=-1;
    end
else
    ncontrasts = size(cfg.contrastcoefs,1);
end

% perform some checks on the configuration
if strcmp(cfg.computeprob,'yes') && strcmp(cfg.computestat,'no')
    ft_error('P-values can only be calculated if the test statistics are calculated.');
end
if ~isfield(cfg,'uvar') || isempty(cfg.uvar)
    ft_error('uvar must be specified for dependent samples statistics');
end

% perform some checks on the design
nuospercond=zeros(nconds,1);
for condindx=1:nconds
    nuospercond(condindx)=length(find(design(cfg.ivar,:)==condindx));
end
if sum(nuospercond)<size(design,2) || any(nuospercond~=nuospercond(1))
  ft_error('Invalid specification of the design array.');
end
nunits = max(design(cfg.uvar,:));
dfdenom = nunits - ncontrasts;
if dfdenom<1
    ft_error('The data must contain more units-of-observation (usually subjects) than the number of contrasts.')
end
nrepl=nunits*nconds;
if (nrepl~=sum(nuospercond)) || (nrepl~=size(dat,2))
  ft_error('Invalid specification of the design array.');
end
nsmpls = size(dat,1);

if strcmp(cfg.computestat,'yes')
% compute the statistic
    % store the positions of the condition labels nunits-by-nconds array
    poslabelsperunit=zeros(nunits,nconds);
    for condindx=1:nconds
        poslabel=find(design(cfg.ivar,:)==condindx);
        [dum,i]=sort(design(cfg.uvar,poslabel),'ascend');
        poslabelsperunit(:,condindx)=poslabel(i);
    end
    % reshape poslabelsperunit into a row vector that contains the
    % replications of the first condition on the first nunits positions,
    % the replications of the second condition on the second nunits
    % positions, etc.
    poslabelsperunit=reshape(poslabelsperunit,1,nrepl);
    s.stat=zeros(nsmpls,1);
    for smplindx=1:nsmpls
        datonesmpl=reshape(dat(smplindx,poslabelsperunit),nunits,nconds);
        contrasts=datonesmpl*cfg.contrastcoefs';
        contrastavg=mean(contrasts,1);
        dev=contrasts-repmat(contrastavg,nunits,1);
        covmat=(dev'*dev)/(nunits-1);
        s.stat(smplindx)=nunits*contrastavg*inv(covmat)*contrastavg';
    end
end

if strcmp(cfg.computecritval,'yes')
  % also compute the critical values
  s.dfnum = ncontrasts;
  s.dfdenom = nunits - ncontrasts;
  if cfg.tail==-1
      ft_error('For a dependent samples F-statistic, it does not make sense to calculate a left tail critical value.');
  end
  if cfg.tail==0
      ft_error('For a dependent samples F-statistic, it does not make sense to calculate a two-sided critical value.');
  end
  if cfg.tail==1
    s.critval = ((nunits-1).*ncontrasts./(nunits-ncontrasts)).*finv(1-cfg.alpha,s.dfnum,s.dfdenom);
  end
end

if strcmp(cfg.computeprob,'yes')
  % also compute the p-values
  s.dfnum = ncontrasts;
  s.dfdenom = nunits - ncontrasts;
  if cfg.tail==-1
      ft_error('For a dependent samples F-statistic, it does not make sense to calculate a left tail p-value.');
  end
  if cfg.tail==0
      ft_error('For a dependent samples F-statistic, it does not make sense to calculate a two-sided p-value.');
  end
  if cfg.tail==1
    scaledstat = ((nunits-ncontrasts)./((nunits-1).*ncontrasts)).*s.stat;
    s.prob = 1-fcdf(scaledstat,s.dfnum,s.dfdenom);
  end
end


function x = loop(x, continue_fcn, f, cleanup_fcn)

% x = loop(x, continue_fcn, f, cleanup_fcn)
%
% Looping function. Provides the ability to treating "looping" as a
% function instead of a command.
%
% See functional_programming_examples.m for a discussion of this function.
%
% Inputs:
% x            - Initial state (can be cell array of arguments to f)
% continue_fcn - Continue function, returns true iff the loop should go on
% f            - Function of the state (x) to run every iteration
% cleanup_fcn  - Function to select what to return from the state when the
%                looping is complete (optional)
%
% Outputs:
% x        - The updated state. If a cleanup function is supplied, this
%            will be cleanup(x).
%
% Fibonacci example:
%
% fib = @(n) loop({1, 0, 1}, ...          % {k, fib(k-1), fib(k)}
%                 @(x) x{1} < n, ...      % While k < n
%                 @(x) {x{1} + 1, ...     %   Increase k
%                       x{3}, ...         %   Store fib(k-1)
%                       x{3} + x{2}}, ... %   Store fib(k-1) + fib(k-2)
%                 @(x) x{3})              % End, returning only fib(k)
%
% arrayfun(fib, 1:10)
%
% Tucker McClure
% Copyright 2013 The MathWorks, Inc.

    while tern(iscell(x), @()continue_fcn(x{:}), @()continue_fcn(x))
        if iscell(x)
            x = f(x{:});
        else
            x = f(x);
        end
    end

    if nargin == 4
        if iscell(x)
            x = cleanup_fcn(x{:});
        else
            x = cleanup_fcn(x);
        end
    end

end


function [A] = au2A(au)
% Convert length from astronomical units to angstroms.
% Chad A. Greene 2012
A = au*1.49597870691e+21;


function [x0,LB,UB,PLB,PUB] = boundscheck_vbmc(x0,LB,UB,PLB,PUB,prnt)
%BOUNDSCHECK Initial check of bounds.

[N0,nvars] = size(x0);

% Expand scalar inputs to full vectors
if isscalar(LB); LB = LB*ones(1,nvars); end
if isscalar(UB); UB = UB*ones(1,nvars); end
if isscalar(PLB); PLB = PLB*ones(1,nvars); end
if isscalar(PUB); PUB = PUB*ones(1,nvars); end

if isempty(PLB) || isempty(PUB)
    if N0 > 1
        if prnt > 0
            fprintf('PLB and/or PUB not specified. Estimating plausible bounds from starting set X0...\n');
        end
        width = max(x0) - min(x0);
        if isempty(PLB)
            PLB = min(x0) - width/N0;
            PLB = max(PLB,LB);
        end
        if isempty(PUB)
            PUB = max(x0) + width/N0;
            PUB = min(PUB,UB);
        end

        idx = any(PLB == PUB);
        if any(idx)
            PLB(idx) = LB(idx);
            PUB(idx) = UB(idx);
            warning('vbmc:pbInitFailed', ...
                'Some plausible bounds could not be determined from starting set. Using hard upper/lower bounds for those instead.')
        end
    else
        warning('vbmc:pbUnspecified', ...
            'Plausible lower/upper bounds PLB and/or PUB not specified and X0 is not a valid starting set. Using hard upper/lower bounds instead.');
        if isempty(PLB); PLB = LB; end
        if isempty(PUB); PUB = UB; end
    end
end

% Test that all vectors have the same length
if any([numel(LB),numel(UB),numel(PLB),numel(PUB)] ~= nvars)
    error('All input vectors (X0, LB, UB, PLB, PUB), if specified, need to have the same size.');
end

% Test that all vectors are row vectors
if ~isvector(LB) || ~isvector(UB) || ~isvector(PLB) || ~isvector(PUB) ...
        || size(LB,1) ~= 1 || size(UB,1) ~= 1 || size(PLB,1) ~= 1 || size(PUB,1) ~= 1
    error('All input vectors LB, UB, PLB, PUB, if specified, should be row vectors.');
end

% Test that plausible bounds are finite
if ~all(isfinite(([PLB, PUB])))
    error('Plausible interval bounds PLB and PUB need to be finite.');
end

% Test that all vectors are real-valued
if ~isreal([x0(:)', LB, UB, PLB, PUB])
    error('All input vectors should be real-valued.');
end

% Fixed variables (all bounds equal) are not supported
fixidx = (LB == UB) & (UB == PLB) & (PLB == PUB);
if any(fixidx)
    error('vbmc:FixedVariables', ...
        'VBMC does not support fixed variables. Lower and upper bounds should be different.');
end

% Test that plausible bounds are different
if any(PLB == PUB)
    error('vbmc:MatchingPB', ...
        'For all variables, plausible lower and upper bounds need to be distinct.')
end

% Check that all X0 are inside the bounds
if any(any(bsxfun(@lt,x0,LB))) || any(any(bsxfun(@gt,x0,UB)))
    error('vbmc:InitialPointsNotInsideBounds', ...
        'The starting points X0 are not inside the provided hard bounds LB and UB.');
end

% Compute "effective" bounds (slightly inside provided hard bounds)
bounds_range = UB - LB;
bounds_range(isinf(bounds_range)) = 1e3;
scale_factor = 1e-3;
LB_eff = LB + scale_factor*bounds_range;
LB_eff(abs(LB) <= realmin) = scale_factor*bounds_range(abs(LB) <= realmin);
UB_eff = UB - scale_factor*bounds_range;
UB_eff(abs(UB) <= realmin) = -scale_factor*bounds_range(abs(UB) <= realmin);
LB_eff(isinf(LB)) = LB(isinf(LB));  % Infinities stay the same
UB_eff(isinf(UB)) = UB(isinf(UB));

if any(LB_eff >= UB_eff)
    error('vbmc:StrictBoundsTooClose', ...
        'Hard bounds LB and UB are numerically too close. Make them more separate.');
end

% Fix when provided X0 are almost on the bounds -- move them inside
if any(any(bsxfun(@le,x0,LB_eff))) || any(any(bsxfun(@ge,x0,UB_eff)))
    warning('vbmc:InitialPointsTooClosePB', ...
        'The starting points X0 are on or numerically too close to the hard bounds LB and UB. Moving the initial points more inside...');
    x0 = bsxfun(@max, bsxfun(@min,x0,UB_eff), LB_eff);
end

% Test order of bounds (permissive)
ordidx = LB <= PLB & PLB < PUB & PUB <= UB;
if any(~ordidx)
    error('vbmc:StrictBounds', ...
        'For each variable, hard and plausible bounds should respect the ordering LB < PLB < PUB < UB.');
end

% Test that plausible bounds are reasonably separated from hard bounds
ordidx = LB_eff < PLB & PUB < UB_eff;
if any(~ordidx)
    warning('vbmc:TooCloseBounds', ...
        'For each variable, hard and plausible bounds should not be too close. Moving plausible bounds.');
    PLB = max(PLB,LB_eff);
    PUB = min(PUB,UB_eff);
end

% Check that all X0 are inside the plausible bounds, move bounds otherwise
if any(any(bsxfun(@le,x0,PLB))) || any(any(bsxfun(@ge,x0,PUB)))
    warning('vbmc:InitialPointsOutsidePB', ...
        'The starting points X0 are not inside the provided plausible bounds PLB and PUB. Expanding the plausible bounds...');
    PLB = min(PLB,min(x0,[],1));
    PUB = max(PUB,max(x0,[],1));
end

% Test order of bounds
ordidx = LB < PLB & PLB < PUB & PUB < UB;
if any(~ordidx)
    error('vbmc:StrictBounds', ...
        'For each variable, hard and plausible bounds should respect the ordering LB < PLB < PUB < UB.');
end

% Test that variables are either bounded or unbounded (not half-bounded)
halfbnd = (isinf(LB) & isfinite(UB)) | (isfinite(LB) & isinf(UB));
if any(halfbnd)
    error('vbmc:HalfBounds', ...
        'Each variable needs to be unbounded or bounded. Variables bounded only below/above are not supported.');
end


% dbn - training a DBN with up-down algorithm
% Copyright (C) 2011 KyungHyun Cho, Tapani Raiko, Alexander Ilin
%
% This program is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License
% as published by the Free Software Foundation; either version 2
% of the License, or (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
%
function [D] = dbn(D, patches);

actual_lrate = D.learning.lrate;

n_samples = size(patches, 1);

layers = D.structure.layers;
n_layers = length(layers);

if layers(1) ~= size(patches, 2)
    error('Data is not properly aligned');
end

minibatch_sz = D.learning.minibatch_sz;
n_minibatches = ceil(n_samples / minibatch_sz);

n_epochs = D.iteration.n_epochs;

momentum = D.learning.momentum;
weight_decay = D.learning.weight_decay;

rec.biases_grad = cell(n_layers-1, 1);
rec.W_grad = cell(n_layers-1, 1);
rec.biases_grad_old = cell(n_layers-1, 1);
rec.W_grad_old = cell(n_layers-1, 1);
for l = 1:n_layers-1
    rec.biases_grad{l} = zeros(size(D.rec.biases{l}))';
    if l < n_layers
        rec.W_grad{l} = zeros(size(D.rec.W{l}));
    end
    rec.biases_grad_old{l} = zeros(size(D.rec.biases{l}))';
    if l < n_layers
        rec.W_grad_old{l} = zeros(size(D.rec.W{l}));
    end
end

gen.biases_grad = cell(n_layers-1, 1);
gen.W_grad = cell(n_layers-1, 1);
gen.biases_grad_old = cell(n_layers-1, 1);
gen.W_grad_old = cell(n_layers-1, 1);
for l = 1:n_layers-1
    gen.biases_grad{l} = zeros(size(D.gen.biases{l}))';
    if l < n_layers
        gen.W_grad{l} = zeros(size(D.gen.W{l}));
    end
    gen.biases_grad_old{l} = zeros(size(D.gen.biases{l}))';
    if l < n_layers
        gen.W_grad_old{l} = zeros(size(D.gen.W{l}));
    end
end

top.W_grad = zeros(size(D.top.W));
top.vbias_grad = zeros(size(D.top.vbias));
top.hbias_grad = zeros(size(D.top.hbias));

top.W_grad_old = zeros(size(D.top.W));
top.vbias_grad_old = zeros(size(D.top.vbias));
top.hbias_grad_old = zeros(size(D.top.hbias));

min_recon_error = Inf;
min_recon_error_update_idx = 0;
stopping = 0;

anneal_counter = 0;
actual_lrate0 = actual_lrate;

if D.debug.do_display == 1
    figure(D.debug.display_fid);
end

try
    use_gpu = gpuDeviceCount;
catch errgpu
    use_gpu = false;
    disp(['Could not use CUDA. Error: ' errgpu.identifier])
end

for step=1:n_epochs
    if D.verbose
        fprintf(2, 'Epoch %d/%d: ', step, n_epochs)
    end
    if use_gpu
        % push
        for l = 1:n_layers-1
            if l < n_layers-1
                D.rec.W{l} = gpuArray(single(D.rec.W{l}));
                D.gen.W{l} = gpuArray(single(D.gen.W{l}));
            end
            D.rec.biases{l} = gpuArray(single(D.rec.biases{l}));
            D.gen.biases{l} = gpuArray(single(D.gen.biases{l}));
        end

        D.top.W = gpuArray(single(D.top.W));
        D.top.vbias = gpuArray(single(D.top.vbias));
        D.top.hbias = gpuArray(single(D.top.hbias));
    end

    for mb=1:n_minibatches
        D.iteration.n_updates = D.iteration.n_updates + 1;

        v0 = patches((mb-1) * minibatch_sz + 1:min(mb * minibatch_sz, n_samples), :);
        mb_sz = size(v0,1);

        if use_gpu > 0
            v0 = gpuArray(single(v0));
        end

        % learning rate
        if D.learning.lrate_anneal > 0 && (step >= D.learning.lrate_anneal * n_epochs)
            anneal_counter = anneal_counter + 1;
            actual_lrate = actual_lrate0 / anneal_counter;
        else
            if D.learning.lrate0 > 0
                actual_lrate = D.learning.lrate / (1 + D.iteration.n_updates / D.learning.lrate0);
            else
                actual_lrate = D.learning.lrate;
            end
            actual_lrate0 = actual_lrate;
        end

        D.signals.lrates = [D.signals.lrates actual_lrate];

        % recognition (wake)
        h0r = cell(n_layers-1, 1);
        h0r{1} = v0;
        if D.learning.ffactored == 0
            h0r{1} = binornd(1, h0r{1});
        end

        for l = 2:(n_layers-1)
            h0r{l} = sigmoid(bsxfun(@plus, h0r{l-1} * D.rec.W{l-1}, D.rec.biases{l}'));
            if D.learning.ffactored == 0
                h0r{l} = binornd(1, h0r{l});
            end
        end

        r0 = cell(n_layers-1, 1);
        for l = 1:(n_layers-2)
            r0{l} = sigmoid(bsxfun(@plus, h0r{l+1} * D.gen.W{l}', D.gen.biases{l}'));
        end

        % update generative weight
        for l = 1:(n_layers-2)
            gen.W_grad{l} = ((h0r{l} - r0{l})' * h0r{l+1}) / size(v0, 1);
            gen.W_grad_old{l} = (1 - momentum) * gen.W_grad{l} + momentum * gen.W_grad_old{l};
            D.gen.W{l} = D.gen.W{l} + actual_lrate * (gen.W_grad_old{l} - D.learning.weight_decay * D.gen.W{l});

            gen.biases_grad{l} = mean(h0r{l} - r0{l}, 1);
            gen.biases_grad_old{l} = (1 - momentum) * gen.biases_grad{l} + momentum * gen.biases_grad_old{l};
            D.gen.biases{l} = D.gen.biases{l} + actual_lrate * (gen.biases_grad_old{l}' - D.learning.weight_decay * D.gen.biases{l});
        end

        % contrastive steps
        if D.learning.persistent_cd == 0 || D.iteration.n_updates == 1
            vtop = h0r{end};
        end
        for cs = 1:D.learning.contrastive_step
            htop = sigmoid(bsxfun(@plus, vtop * D.top.W, D.top.hbias'));
            htop = binornd(1, htop);
            vtop = sigmoid(bsxfun(@plus, htop * D.top.W', D.top.vbias'));
            vtop = binornd(1, vtop);
        end

        tv0 = h0r{end};
        th0 = sigmoid(bsxfun(@plus, tv0 * D.top.W, D.top.hbias'));
        tv1 = vtop;
        th1 = sigmoid(bsxfun(@plus, tv1 * D.top.W, D.top.hbias'));

        top.W_grad = (tv0' * th0)/size(tv0,1) - (tv1' * th1)/size(tv1,1);
        top.vbias_grad = mean(tv0, 1) - mean(tv1, 1);
        top.hbias_grad = mean(th0, 1) - mean(th1, 1);

        top.W_grad_old = (1 - momentum) * top.W_grad + momentum * top.W_grad_old;
        top.vbias_grad_old = (1 - momentum) * top.vbias_grad' + momentum * top.vbias_grad_old;
        top.hbias_grad_old = (1 - momentum) * top.hbias_grad' + momentum * top.hbias_grad_old;

        D.top.W = D.top.W + actual_lrate * (top.W_grad_old - D.learning.weight_decay * D.top.W);
        D.top.vbias = D.top.vbias + actual_lrate * (top.vbias_grad_old - D.learning.weight_decay * D.top.vbias);
        D.top.hbias = D.top.hbias + actual_lrate * (top.hbias_grad_old - D.learning.weight_decay * D.top.hbias);

        % generation (sleep)
        h0r{n_layers-1} = tv1;
        for l = (n_layers-2):-1:1
            h0r{l} = sigmoid(bsxfun(@plus, h0r{l+1} * D.gen.W{l}', D.gen.biases{l}'));
            if D.learning.ffactored == 0
                h0r{l} = binornd(1, h0r{l});
            end
        end

        for l = 2:(n_layers - 1)
            r0{l} = sigmoid(bsxfun(@plus, h0r{l-1} * D.rec.W{l-1}, D.rec.biases{l}'));
        end

        % update recognition weight
        for l = 1:(n_layers-1)
            if l < n_layers - 1
                rec.W_grad{l} = (h0r{l}' * (h0r{l+1} - r0{l+1})) / size(h0r{1}, 1);
                rec.W_grad_old{l} = (1 - momentum) * rec.W_grad{l} + momentum * rec.W_grad_old{l};
                D.rec.W{l} = D.rec.W{l} + actual_lrate * (rec.W_grad_old{l} - D.learning.weight_decay * D.rec.W{l});
            end

            if l > 1
                rec.biases_grad{l} = mean(h0r{l} - r0{l}, 1);
                rec.biases_grad_old{l} = (1 - momentum) * rec.biases_grad{l} + momentum * rec.biases_grad_old{l};
                D.rec.biases{l} = D.rec.biases{l} + actual_lrate * (rec.biases_grad_old{l}' - D.learning.weight_decay * D.rec.biases{l});
            end
        end

        clear h0r r0 htop tv0 th0 tv1 th1;
        if D.learning.persistent_cd  == 0
            clear vtop;
        end

        % compute reconstruction error
        hr = v0;
        for l = 2:n_layers-1
            hr = sigmoid(bsxfun(@plus, hr * D.rec.W{l-1}, D.rec.biases{l}'));
        end
        hr = sigmoid(bsxfun(@plus, hr * D.top.W, D.top.hbias'));
        hr = sigmoid(bsxfun(@plus, hr * D.top.W', D.top.vbias'));
        for l = n_layers-1:-1:2
            hr = sigmoid(bsxfun(@plus, hr * D.gen.W{l-1}', D.gen.biases{l-1}'));
        end

        rerr = mean(sum((v0 - hr).^2,2));
        if use_gpu > 0
            rerr = gather(rerr);
        end
        D.signals.recon_errors = [D.signals.recon_errors rerr];

        if D.verbose == 1
            fprintf(2, '.');
        end

        if use_gpu > 0
            clear v0 h0d h0e v0_clean vr hr deltae deltad
        end

        if D.stop.criterion > 0
            if D.stop.criterion == 1
                if min_recon_error > D.signals.recon_errors(end)
                    min_recon_error = D.signals.recon_errors(end);
                    min_recon_error_update_idx = D.iteration.n_updates;
                else
                    if D.iteration.n_updates > min_recon_error_update_idx + D.stop.recon_error.tolerate_count
                        fprintf(2, '\nStopping criterion reached (recon error) %f > %f\n', ...
                            D.signals.recon_errors(end), min_recon_error);
                        stopping = 1;
                        break;
                    end
                end
            else
                error ('Unknown stopping criterion %d', D.stop.criterion);
            end
        end

        if length(D.hook.per_update) > 1
            err = D.hook.per_update{1}(D, D.hook.per_update{2});

            if err == -1
                stopping = 1;
                break;
            end
        end

        if D.debug.do_display == 1 && mod(D.iteration.n_updates, D.debug.display_interval) == 0
            D.debug.display_function (D.debug.display_fid, D, v0, v1, h0, h1, W_grad, vbias_grad, hbias_grad);
            drawnow;
        end
    end

    if use_gpu > 0
        % pull
        for l = 1:n_layers-1
            if l < n_layers-1
                D.rec.W{l} = gather(D.rec.W{l});
                D.gen.W{l} = gather(D.gen.W{l});
            end
            D.rec.biases{l} = gather(D.rec.biases{l});
            D.gen.biases{l} = gather(D.gen.biases{l});
        end

        D.top.W = gather(D.top.W);
        D.top.vbias = gather(D.top.vbias);
        D.top.hbias = gather(D.top.hbias);
    end

    if length(D.hook.per_epoch) > 1
        err = D.hook.per_epoch{1}(D, D.hook.per_epoch{2});

        if err == -1
            stopping = 1;
        end
    end

    if stopping == 1
        break;
    end

    if D.verbose == 1
        fprintf(2, '\n');
    end

    fprintf(2, 'Epoch %d/%d - recon_error: %f\n', step, n_epochs, ...
        D.signals.recon_errors(end));
end

if use_gpu > 0
    % pull
    for l = 1:n_layers-1
        if l < n_layers-1
            D.rec.W{l} = gather(D.rec.W{l});
            D.gen.W{l} = gather(D.gen.W{l});
        end
        D.rec.biases{l} = gather(D.rec.biases{l});
        D.gen.biases{l} = gather(D.gen.biases{l});
    end

    D.top.W = gather(D.top.W);
    D.top.vbias = gather(D.top.vbias);
    D.top.hbias = gather(D.top.hbias);
end


function gammaM = gammaScan3d(doseArray1, doseArray2, deltaXYZv, doseAgreement, distAgreement, maxDistance)
% function gammaM = gammaScan3d(doseArray1, doseArray2, deltaXYZv, doseAgreement, distAgreement, maxDistance)
%
% APA, 04/27/2012

deltaX = deltaXYZv(1);
deltaY = deltaXYZv(1);
deltaZ = deltaXYZv(1);
incrementRadius = min([deltaX deltaY deltaZ]);

% Initial gamma
gammaM = ((doseArray1-doseArray2).^2).^0.5/doseAgreement;
convergedM = false(size(gammaM));

% Calculate until 4 times the permissible distance to agreement.
if ~exist('maxDistance', 'var')
    maxDistance = distAgreement*1.5;
end
outerRadiusV = incrementRadius:incrementRadius:maxDistance;
numIters = length(outerRadiusV);

siz = size(gammaM);
minDoseDiffM = zeros(siz,'single');
maxDoseDiffM = minDoseDiffM;

% convergenceCountM = zeros(siz,'uint8');

% Update waitbar on gamma GUI
gammaGUIFig = findobj('tag','CERRgammaInputGUI');
if ~isempty(gammaGUIFig)
    ud = get(gammaGUIFig,'userdata');
    set(ud.wb.patch,'xData',[0 0 0 0])
end

for radNum = 1:numIters

    disp(['--- Gamma Calculation Iteraion ', num2str(radNum), ' ----'])

    % Create an ellipsoid ring neighborhood
    outerRadius = outerRadiusV(radNum);

    rowOutV = floor(outerRadius/deltaY);
    colOutV = floor(outerRadius/deltaX);
    slcOutV = floor(outerRadius/deltaZ);

    NHOOD_outer = createEllipsoidNHOOD(1:rowOutV,1:colOutV,1:slcOutV);

    % Compute Min and Max for the ellipsoid neighborhood
    [minLocalM, maxLocalM] = getMinMaxIM(doseArray2,NHOOD_outer);

    % Compute difference between dose1 and (min,max) for dose2
    minDoseDiffM(~convergedM) = doseArray1(~convergedM) - minLocalM(~convergedM) + doseAgreement; % D1-(min(D2)-5)
    maxDoseDiffM(~convergedM) = maxLocalM(~convergedM) + doseAgreement - doseArray1(~convergedM); % (max(D2)+5)-D1

    % If dose1 is contained within (min,max) for dose2, then it converged!
    newConvergedM = ~convergedM & minDoseDiffM >= 0 & maxDoseDiffM >= 0;

    % Compute gamma for voxels that converged
    %gammaM(newConvergedM) =  min(gammaM(newConvergedM), outerRadius/distAgreement);
    gammaM(newConvergedM) =  outerRadius;

    % Add newly converged voxels to the list
    convergedM = convergedM | newConvergedM;

    % Compute gamma for voxels that have not yet converged
    %gammaForNotConvergedM =  (min((minDoseDiffM(~convergedM)-doseAgreement).^2, (maxDoseDiffM(~convergedM)-doseAgreement).^2)./doseAgreement^2 + (outerRadius/distAgreement)^2).^0.5;
    %gammaM(~convergedM) = min(gammaM(~convergedM), gammaForNotConvergedM);

    % If gamma is less or equal to outerRadius/distAgreement, then the voxel conveged!
    %convergedM(~convergedM) = gammaM(~convergedM) <= outerRadius/distAgreement;

    if ~isempty(gammaGUIFig)
        set(ud.wb.patch,'xData',[0 0 radNum/numIters radNum/numIters])
        drawnow
    end

    if all(convergedM(:))
        disp('All converged!!!')
        if ~isempty(gammaGUIFig)
            set(ud.wb.patch,'xData',[0 0 1 1])
        end
        break
    end

end

gammaM(~convergedM) = NaN;


function [beta_gibbs, sigma_gibbs, theta_gibbs, ss_record,indH,beta_theta_gibbs]=TVEmagibbs(data_endo,It,Bu,beta0,omega0,psi0,lambda0,Y,X,n,T,k1,q1,p,regimeperiods,names,TVEH)

% function [beta_gibbs psi_gibbs sigma_gibbs ss_record delta_gibbs]=magibbs(data_endo,data_exo,It,Bu,beta0,omega0,psi0,lambda0,Y,X,Z,n,m,T,k1,k3,q1,q2,q3,p)
% performs the Gibbs algortihm 3.5.1 for a MABVAR model, and returns draws from posterior distribution
% inputs:  - matrix 'data_endo': the matrix storing the endogenous time series data used to estimate the model
%          - matrix 'data_exo': the matrix storing the exogenous time series data used to estimate the model
%          - integer 'It': the total number of iterations run by the Gibbs sampler
%          - integer 'Bu': the number of initial iterations discared as burn-in sample
%          - vector 'beta0': the vector containing the mean of the prior distribution for beta, defined in (3.5.17)
%          - matrix 'omega0': the variance-covariance matrix for the prior distribution of beta, defined in (3.5.17)
%          - vector 'psi0': the vector containing the mean of the prior distribution for psi, defined in (3.5.19)
%          - matrix 'lambda0': the variance-covariance matrix for the prior distribution of psi, defined in (3.5.19)
%          - matrix 'Y': the matrix of endogenous variables, defined in (3.5.10)
%          - matrix 'X': the matrix of endogenous regressors, defined in (3.5.10)
%          - matrix 'Z': the matrix of exogenous regressors, defined in (3.5.10)
%          - integer 'n': the number of endogenous variables in the model
%          - integer 'm': the number of exogenous variables in the model
%          - integer 'T': the sample size, i.e. the number of time periods used to estimate the model
%          - integer 'k1': the number of coefficients related to the endogenous variables for each equation in the model
%          - integer 'k3': the number of coefficients related to the exogenous variables for each equation, in the reformulated model (3.5.5)
%          - integer 'q1': the total number of VAR coefficients related to the endogenous variables
%          - integer 'q2': the total number of VAR coefficients related to the exogenous variables
%          - integer 'q3': the total number of VAR coefficients related to the exogenous variables, in the reformulated model (3.5.5)
%          - integer 'p': the number of lags in the model
% outputs: - matrix 'beta_gibbs': the matrix recording the post-burn draws of beta
%          - matrix 'psi_gibbs': the matrix recording the post-burn draws of psi
%          - matrix 'sigma_gibbs': the matrix recording the post-burn draws of sigma
%          - matrix 'delta_gibbs': the matrix recording the post-burn draws of delta
%          - cell 'ss_record': the cell storing the post-burn steady-state values

% this function implements algorithm...

% preliminary tasks

% take the alternative exogenous regime
if isempty(regimeperiods)
    data_exo=[ones(size(data_endo,1),1)];
else
data_exo=[ones(size(data_endo,1),1) zeros(size(data_endo,1),1)];
data_exo(find(strcmp(names(2:end,1),regimeperiods(1))):find(strcmp(names(2:end,1),regimeperiods(2))),2)=1;
data_exo(find(strcmp(names(2:end,1),regimeperiods(1))):find(strcmp(names(2:end,1),regimeperiods(2))),1)=0;
end

indH=zeros(T+p,1);
for iR=1:size(data_exo,2)
    indH(data_exo(:,iR)==1)=iR;
end

% create the cell that will store the records for the steady-state
ss_record=cell(n,1); %change

% invert omega0
invomega0=diag(1./diag(omega0));

% invert lambda0
invlambda0=lambda0\eye(length(lambda0));

% step 2: set initial values
% set initial values for B, beta and sigma; as no OLS estimates are available, simply set the value as zeros for B and beta, and identity for sigma
B=zeros(k1,n);

% define the initial value for the inverse of sigma: beacause sigma is identity, this is also identity
invsigma=eye(n);

% preallocate space for the matrix with the equilibrium values
eq=zeros(T+p,n);

q2=length(psi0);

hbar = bear.parfor_progressbar(It,'Progress of the Gibbs sampler');  %create the progress bar

% start iterations
for ii=1:It

hbar.iterate(1);   % update progress by one iteration

% step 3: at iteration ii, draw psi from N, conditional on beta and sigma
% obtain the lambdabar matrix
Ybar=Y-X*B;
Ypsi=bear.vec(Ybar');

t=1;
Fsimple=TVEH(:,:,indH(t+p),t+p);
for k=2:p+1
    Fsimple=Fsimple-B((k-2)*n+1:(k-1)*n,:)'*TVEH(:,:,indH(t+p-(k-1)),t+p-(k-1));
end

for t=2:T
    Ftemp=TVEH(:,:,indH(t+p),t+p);
    for k=2:p+1
        Ftemp=Ftemp-B((k-2)*n+1:(k-1)*n,:)'*TVEH(:,:,indH(t+p-(k-1)),t+p-(k-1));
    end
    Fsimple=cat(1,Fsimple,Ftemp);
end

invOmega=kron(eye(T),invsigma);

CT=(invlambda0+Fsimple'*invOmega*Fsimple)\eye(length(lambda0));
mT=CT*(Fsimple'*invOmega*Ypsi++invlambda0*psi0);

% draw from N(psibar,lambdabar);
theta=mT+chol(bear.nspd(CT),'lower')*randn(q2,1);

% recover equilibrium values from psi
for it=1:T+p
    eq(it,:)=(squeeze(TVEH(:,:,indH(it),it))*theta)'; % compute the equilibrium values given theta
end

% step 4: now that psi/F has been drawn, it is possible to generate Yhat, Xhat and yhat
temp2=data_endo-eq;
temp3=bear.lagx(temp2,p);
Yhat=temp3(:,1:n);
yhat=Yhat(:);
Xhat=temp3(:,n+1:end);

% step 5: next, at iteration ii, draw sigma from IW, conditional on most recent draw for psi and beta
% obtain first Stilde
Stilde=(Yhat-Xhat*B)'*(Yhat-Xhat*B);
% next draw from IW(Stilde,T)
sigma=bear.iwdraw(Stilde,T);
% invert sigma
C=bear.trns(chol(bear.nspd(sigma),'Lower'));
invC=C\speye(n);
invsigma=invC*invC';

% step 6: finally, at iteration ii, draw beta from a N, conditional on most recent draw for psi and sigma
% first obtain the omegabar matrix
invomegabar=invomega0+kron(invsigma,Xhat'*Xhat);
C=bear.trns(chol(bear.nspd(invomegabar),'Lower'));
invC=C\speye(q1);
omegabar=invC*invC';
% following, obtain betabar
betabar=omegabar*(invomega0*beta0+kron(invsigma,Xhat')*yhat);
% draw from N(betabar,omegabar);
beta=betabar+chol(bear.nspd(omegabar),'lower')*randn(q1,1);
% reshape to obtain B
B=reshape(beta,k1,n);
% update U, using the draw obtained for B

   % record the values if the number of burn-in iterations is exceeded
   if ii>Bu
   % record the value of beta
   beta_gibbs(:,ii-Bu)=beta;
   % record the value of sigma (in vectorized form)
   sigma_gibbs(:,ii-Bu)=sigma(:);

   % compute and record the steady-state
   ss=eq;
   % trim p initial conditions to be consistent with the sample
   ss=ss(p+1:end,:);
      % then record in the corresponding cell
      for jj=1:n
      ss_record{jj,1}(ii-Bu,:)=ss(:,jj)';
      end
   % finally, record theta
   theta_gibbs(:,ii-Bu)=theta;

   beta_theta_gibbs(:,ii-Bu)=[beta;theta];
   % if current iteration is still a burn iteration, do not record the result
   else
   end

% step 7: go for next iteration
end

close(hbar);   %close progress bar


function T=findDartboardSubarrays(aperFracs,subarraysPerRing,angShifts,xyPoints)
%%FINDDARTBOARDSUBARRAYS When designing subarrays for a circular aperture,
%          it is noted in [1] that a dartboard pattern tends to work pretty
%          well. It is also suggested that a subarray pattern returned by
%          findBaylissSubarrays be used as a rule of thumb for choosing how
%          many rings and the number of subarrays per ring in the
%          dartboard. When given the positions of elements in a
%
%INPUTS: aperFracs A numRingsX1 or 1XnumRings vector sorted in increasing
%                  order where aperFrac(i) is the fraction of the aperture
%                  size where the ring specified ends. aperFrac(end) must
%                  equal 1 (100% of the aperture size). The aperture width
%                  is taken to be the maximum distance of a point in
%                  xyPoints from the origin.
% subarraysPerRing A numRingsX1 or 1XnumRings vector indicating the number
%                  of subarrays in each ring. These must be positive
%                  integers.
%        angShifts A numRingsX1 or 1XnumRings vector giving angular offsets
%                  in radians by which the first subarray in each ring
%                  begins. This is here to keep the rings from all lining
%                  up, which should improve how the grating lobes of the
%                  subarrays line up. If an empty matrix is passed, then
%                  angular shifts of zero are used.
%         xyPoints A 2XnumPoints set of numPoints points in the aperture
%                  plane corresponding to element positions. The points
%                  should be shifted such that the center of the aperture
%                  is the origin. The width of the aperture is the maximum
%                  distance of any point from the center.
%
%OUTPUTS: T A numSubarraysXnumPoints boolean matrix (a matrix such that
%           T(i,:) is a set of boolean values indicating which elements are
%           in each subarray). The subarrays do not overlap.
%
%EXAMPLE:
%Here, we lay out the elements for a circular array and then we break them
%into a dartboard configuration. We then display the elements with
%different symbols and colors for the subarrays.
% %First, we create a circular array. The element locations are given in
% %terms of the wavelength lambda, so lambda will not appear in the
% %equations for the sum beam.
% xyVals=getShaped2DLattice([49;49],'circular');
%
% aperFracs=[1/5;2/5;3/5;4/5;1];
% subarraysPerRing=[4;4;8;8;8];
% degShifts=[0;2*pi/(2*4);0;2*pi/(2*8);0];
% T=findDartboardSubarrays(aperFracs,subarraysPerRing,degShifts,xyVals);
%
% figure()
% clf
% hold on
% axis square
% numSubarrays=size(T,1);
% dispOpts={'.b','.g','.m','.c','.y','.k','.b','.r',...
%     'xm','xc','xy','xk','xb','xr','xb','xg',...
%     'ob','og','om','oc','oy','ok','ob','or'...
%     '+m','+c','+m','+y','+k','+r','+b','+g'...
%     'sb','sg','sm','sc','sm','sy','sk','sr'};
% for curSubarray=1:numSubarrays
%     points=xyVals(:,T(curSubarray,:)==1);
%     optVal=mod(curSubarray,length(dispOpts))+1;
%     scatter(points(1,:),points(2,:),dispOpts{optVal},'linewidth',2);
% end
% h1=xlabel('x');
% h2=ylabel('y');
% title('Coloring Represents Subarrays')
% set(gca,'FontSize',14,'FontWeight','bold','FontName','Times')
% set(h1,'FontSize',14,'FontWeight','bold','FontName','Times')
% set(h2,'FontSize',14,'FontWeight','bold','FontName','Times')
%
%REFERENCES:
%[1] U. Nickel, "Subarray configurations for digital beamforming with
%    low sidelobes, adaptive interference suppression and superresolution,"
%    1995, FFM-Report (available from Fraunhofer FKIE, Wachtberg, Germany).
%
%September 2016 David F. Crouse, Naval Research Laboratory, Washington D.C.
%(UNCLASSIFIED) DISTRIBUTION STATEMENT A. Approved for public release.

numRings=length(aperFracs);
numSubarrays=sum(subarraysPerRing);
numEls=size(xyPoints,2);

if(aperFracs(numRings)~=1)
   error('The last element in  aperFracs must be one');
end

if(isempty(angShifts))
    angShifts=zeros(numRings,1);
end

%The maximum squared distance from the origin to a point is taken to be the
%radius of the aperture squared.
a=sqrt(max(sum(xyPoints.^2,1)));

temp=cumsum(subarraysPerRing(:));
numSubsInPriorRings=[0;temp(1:(numRings-1))];

T=false(numSubarrays,numEls);
for curEl=1:numEls
    %The fractional distance from the outer edge of the aperture where the
    %element is located.
    fracCur=norm(xyPoints(:,curEl))/a;

    %Determine which ring the element is in.
    [~, ringIdx]=binSearch(aperFracs,fracCur,2);

    %Each ring can have multiple subarrays. The subarrays are uniformly
    %spaced in angle, but the starting angle is not taken to be zero but
    %rather degShifts(ringIdx) radians.
    angle=atan2(xyPoints(2,curEl),xyPoints(1,curEl));

    subArrayWidth=2*pi/subarraysPerRing(ringIdx);

    subArrayInRing=fix(wrapRange(angle+angShifts(ringIdx),0,2*pi)/subArrayWidth)+1;
    subArrayIdx=numSubsInPriorRings(ringIdx)+subArrayInRing;
    T(subArrayIdx,curEl)=1;
end
end

%LICENSE:
%
%The source code is in the public domain and not licensed or under
%copyright. The information and software may be used freely by the public.
%As required by 17 U.S.C. 403, third parties producing copyrighted works
%consisting predominantly of the material produced by U.S. government
%agencies must provide notice with such work(s) identifying the U.S.
%Government material incorporated and stating that such material is not
%subject to copyright protection.
%
%Derived works shall not identify themselves in a manner that implies an
%endorsement by or an affiliation with the Naval Research Laboratory.
%
%RECIPIENT BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE
%SOFTWARE AND ANY RELATED MATERIALS, AND AGREES TO INDEMNIFY THE NAVAL
%RESEARCH LABORATORY FOR ALL THIRD-PARTY CLAIMS RESULTING FROM THE ACTIONS
%OF RECIPIENT IN THE USE OF THE SOFTWARE.


function [result]=sv_NodeCalc(mCatalog, fSplitTime, bTimePeriod, fTimePeriod, nCalculateMC, vCluster, bDecluster, fMcOrg, fBValueOrg, bSeisModel)
% function [result]=sv_NodeCalc(mCatalog, fSplitTime, bTimeperiod, fTimePeriod, nCalculateMC, vCluster, bDecluster, fMcOrg, fBValueOrg, bSeisModel)
% -------------------------------------------------------------------------------------------------------------------------------------------------
% Function to calculate absolute difference of number of events between
% to time periods of an earthquake catalog normalized to a year
% Incoming variables:
% mCatalog     : current earthquake catalog
% fSplitTime   : Splitting time
% bTimePeriod  : Use specific time period to compare (1) or from beginning till end of catalog (0)
% fTimePeriod  : Time period in days to be compared before and after fSplitTime
% nCalculateMC : Number for Method of Mc determination
% vCluster     : Vector of cluster numbers
% bDecluster   : Decluster catalog or not
% fMcOrg       : Mc of entire catalog
% fBValueOrg   : b-value of entire catalog
% bSeisModel   : Method to model seismicity varation: 0 - b-value fittng, 1 - grid search
%
% Outgoing variable:
% result.dNdiffsum      : total difference of number of events in the two time periods
% result.fMc            : magnitude of completeness for entire catalog
% result.fMcFirstPeriod : magnitude of completeness for first period defined
% result.fMcSecondPeriod : magnitude of completeness for second period defined
% result.fdMc            : Difference in Mc: Mc(Per1)-Mc(Per2)
% result.fMshift         : Simple magnitude shift
% result.fMshiftFit      : Goodness of fit in percent to modeling second period with simple magnitude shift of first period
% result.fMshiftSig      : Simple magnitude shift at 99% significance level of z-statistic
% result.fFactorHi       : Mean rate factor for EQ >= Mc(Per1) between two periods
% result.fPerHi          : Percental seismicity change
% result.fFactorLow      : Mean rate factor for EQ < Mc(Per1) between two periods
% result.fPerLow         : Percental seismicity change
% result.fMRateFit       : Goodness of fit by modeling second period with rate factor multiplication of first period
% result.fMshiftCom      : Magnitude shift combined with stretch
% result.fStretch        : Magnitude stretch
% result.fMTransFit      : Goodness of fit by modeling second period manipulating the first period
% result.fdMag           : Magnitude shift for a M=1 EQ using result.fMshiftCom and result.fStretch
% result.mModelCat       : Magnitude alternation for a magnitude M=1 EQ
%
% Author: J. Woessner
% woessner@seismo.ifg.ethz.ch
% last update: 01.11.02
% Changes:
% 19.08.02: Replaced  fcumulsum.m with calc_cumulsum.m
% 08.10.02: Deleted return at the end.

% Changes:
% 19.08.02: Replaced  fcumulsum.m with calc_cumulsum.m
% 08.10.02: Deleted return at the end.

% Init variable
result=[];
fTimePeriod =fTimePeriod/365;

if bDecluster == 1
    % Determine degree of Poissonian distribution using Chi^2-Test
    [result.fChi2 result.fChi2_sig90 result.fChi2_sig95 result.Chi2_sig99 result.nPoissDeg]= calc_Chi2(mCatalog);
    vSel = (vCluster(:,1) > 0);
    mCatalogDecl = mCatalog(~vSel,:);
    [result.fChi2_Dec result.fChi2_sig90_Dec result.fChi2_sig95_Dec result.Chi2_sig99_Dec...
            result.nPoissDeg_Dec]= calc_Chi2(mCatalogDecl);
    % Determine degree of Clustering
    [result.fClusterDeg] = calc_ClusterDeg(mCatalog, vCluster);
elseif (bSeisModel == 1)
    % Create the catalogs for the two time peiods
    [result.mFirstCatalog, result.mSecondCatalog, result.fFirstPeriodExact, result.fSecondPeriodExact, result.fFirstPeriod,...
            result.fSecondPeriod] = ex_SplitCatalog(mCatalog, fSplitTime, bTimePeriod, fTimePeriod, bTimePeriod, fTimePeriod);

    %% Determination of difference between two time periods normalized to year
    [dEv_val dMags dEv_valsum dEv_valsum_rev,  dMags_rev] = calc_cumulsum(result.mFirstCatalog);
    [dEv_val2 dMags2 dEv_valsum2 dEv_valsum_rev2,  dMags_rev2] = calc_cumulsum(result.mSecondCatalog);
    result.dNdiff = max(dEv_val2)/result.fSecondPeriodExact-max(dEv_val)/result.fFirstPeriodExact; % Normalization
    result.dNdiffsum = cumsum(result.dNdiff');
    %result.dNdiffsumVal = result.dNdiffsum(length(result.dNdiffsum));

    % Determine Mc for entire catalog, two time periods and difference in Mc
    result.fMc = calc_Mc(mCatalog, nCalculateMC);
    if isempty(result.fMc)
        result.fMc = NaN;
    end
    result.fMcFirstPeriod = calc_Mc(result.mFirstCatalog, nCalculateMC);
    if isempty(result.fMcFirstPeriod)
        result.fMcFirstPeriod = NaN;
    end
    result.fMcSecondPeriod = calc_Mc(result.mSecondCatalog, nCalculateMC);
    if isempty(result.fMcSecondPeriod)
        result.fMcSecondPeriod = NaN;
    end
    result.fdMc = result.fMcSecondPeriod - result.fMcFirstPeriod;

    % Determine simple magnitude shift and Goodness of Fit
    [result.fMshift result.fMshiftFit] = calc_Magshift(mCatalog, fSplitTime, bTimePeriod, fTimePeriod, nCalculateMC);
    if isempty(result.fMshift)
        result.fMshift = NaN;
        result.fMshiftFit = NaN;
    end
    % Determine significant Mshift at 99% level
    [mLMagsig mHMagsig fLZmax fLZmean fLZmin fHZmax fHZmean,  fHZmin] =...
        calc_Magsig(result.mFirstCatalog, result.mSecondCatalog , result.fFirstPeriodExact, result.fSecondPeriodExact, 0.1);
    if ((fLZmax >= 2.57 & fHZmin <= -2.57) | fLZmin <= -2.57 & fHZmax >= 2.57)
        result.fMshiftSig = result.fMshift;
    else
        result.fMshiftSig = NaN;
    end
    % Determine rate factors and percentage change of EQ for M<Mc and M>=Mc
    [result.fFactorHi fStdHi fResHi result.fPerHi result.fFactorLow fStdLow fResLow result.fPerLow result.fMRateFit]...
        = calc_ratefac(result.mFirstCatalog, result.mSecondCatalog, fTimePeriod, fTimePeriod,...
        result.fMcFirstPeriod, result.fMcSecondPeriod);
    [result.fMshiftCom result.fStretch result.fMTransFit result.fARate] = calc_Shiftstretch(mCatalog, fSplitTime, bTimePeriod,...
        fTimePeriod, nCalculateMC, fMcOrg, fBValueOrg);
    %% Magnitude fit for a magnitude M=1 EQ
    result.fdMag = 1+((result.fStretch-1)+result.fMshiftCom); % Theoretical map
    result.mModelCat = result.mFirstCatalog;
    result.mModelCat(:,6) = result.fStretch.*result.mModelCat(:,6)+result.fMshiftCom;
else
    % Create the catalogs for the two time peiods
    [result.mFirstCatalog, result.mSecondCatalog, result.fFirstPeriodExact, result.fSecondPeriodExact, result.fFirstPeriod,...
            result.fSecondPeriod] = ex_SplitCatalog(mCatalog, fSplitTime, bTimePeriod, fTimePeriod, bTimePeriod, fTimePeriod);

    %% Determination of difference between two time periods normalized to year
    [dEv_val dMags dEv_valsum dEv_valsum_rev,  dMags_rev] = calc_cumulsum(result.mFirstCatalog);
    [dEv_val2 dMags2 dEv_valsum2 dEv_valsum_rev2,  dMags_rev2] = calc_cumulsum(result.mSecondCatalog);
%     result.dNdiff = max(dEv_val2)/result.fSecondPeriodExact-max(dEv_val)/result.fFirstPeriodExact; % Normalization
    result.dNdiff = max(dEv_valsum2)/result.fSecondPeriodExact-max(dEv_valsum)/result.fFirstPeriodExact;
    result.dNdiffsum = max(cumsum(result.dNdiff'));
    %result.dNdiffsumVal = result.dNdiffsum(length(result.dNdiffsum));
    if isempty(result.dNdiff)
        result.dNdiff = NaN;
    end
    if isempty(result.dNdiffsum)
        result.dNdiffsum = NaN;
    end
    % Determine Mc for entire catalog, two time periods and difference in Mc
    result.fMc = calc_Mc(mCatalog, nCalculateMC);
    if isempty(result.fMc)
        result.fMc = NaN;
    end
    result.fMcFirstPeriod = calc_Mc(result.mFirstCatalog, nCalculateMC);
    if isempty(result.fMcFirstPeriod)
        result.fMcFirstPeriod = NaN;
    end
    result.fMcSecondPeriod = calc_Mc(result.mSecondCatalog, nCalculateMC);
    if isempty(result.fMcSecondPeriod)
        result.fMcSecondPeriod = NaN;
    end
    result.fdMc = result.fMcSecondPeriod - result.fMcFirstPeriod;

    % Mc determination by grid search
    %[result.fProbMcGrid result.fMcGrid] = calc_McGridN(mCatalog,0.1);
    [result.fProbMcGrid result.fMcGrid] = calc_McCdf2(mCatalog,0.1)
    if (isempty(result.fProbMcGrid) | isempty(result.fMcGrid))
        result.fProbMcGrid = NaN;
        result.fMcGrid = NaN;
    end
    %% Use data only above completeness
    %fMinMc = min([result.fMcFirstPeriod result.fMcSecondPeriod]);
    %     vSel1 = (result.mFirstCatalog(:,6) >= result.fMcFirstPeriod);
    %     vSel2 = (result.mSecondCatalog(:,6) >= result.fMcFirstPeriod);
    %     result.mFirstCatalog = result.mFirstCatalog(vSel1,:);
    %     result.mSecondCatalog = result.mSecondCatalog(vSel2,:);

    %% Seismicity variation modelling
    %!!!!! NO MODELS WITH STRETCH ALLOWED !!!!!!!!!!!!!!!!!!!!!!!!!
    %%% Maximum likelihood scores and BICs
    [fProb_nochange, fBic_nochange] = calc_loglikelihood_nochange2(result.mFirstCatalog, result.mSecondCatalog);
    [fdM, fProb_dM, fBic_dM, mLikeli_dM] = calc_loglikelihood_dM2(result.mFirstCatalog, result.mSecondCatalog);
    %[fS, fProb_stretch, fBic_stretch, mLikeli_dS] = calc_loglikelihood_stretch(result.mFirstCatalog, result.mSecondCatalog);
    [fFac, fProb_Rate, fBic_Rate, mLikeli_Rate] = calc_loglikelihood_rate2(result.mFirstCatalog, result.mSecondCatalog);
    [fdM_rate, fdM_Fac, fProb_dMrate, fBic_dMrate, mLikeli_dMrate] = calc_loglikelihood_dM_rate2(result.mFirstCatalog, result.mSecondCatalog);
    %[fdS_rate, fdS_Fac, fProb_dSrate, fBic_dSrate mLikeli_dSrate] = calc_loglikelihood_stretch_rate(result.mFirstCatalog, result.mSecondCatalog);
    %[fdM_st, fStretch, fProb_Trans, fBic_Trans, mLikeli_Trans] = calc_loglikelihood_Trans(result.mFirstCatalog, result.mSecondCatalog);
    %[fdM_all, fdS_all, fFac_all, fProb_all, fBic_all, mLikeli_all] = calc_loglikelihood_dMdSrate(result.mFirstCatalog, result.mSecondCatalog);

    %vBic = [fBic_nochange; fBic_dM; fBic_stretch; fBic_Rate; fBic_dMrate; fBic_Trans; fBic_dSrate; fBic_all];
    vBic = [fBic_nochange; fBic_dM;  fBic_Rate; fBic_dMrate];
    [result.nType] = find(vBic == min(vBic));
    %% Initialize result
    result.fdM = nan; % Shift
    result.fdS = nan; % Stretch
    result.fRf = nan; % rate factor
    if length(result.nType) > 1
        vBic;
        result.nModelChoice = NaN;
    elseif length(result.nType) == 0
        result.nModelChoice = nan;
    else
        switch result.nType
        case 1
            result.nModelChoice = 1;
            %     result.fdM = 0; % Shift
            %     result.fdS = 1; % Stretch
            %     result.fRf = 1; % rate factor
        case 2
            result.nModelChoice = 2;
            result.fdM = fdM;
%         case 3
%             result.nModelChoice = 3;
%             result.fdS = fS;
        case 3
            result.nModelChoice = 3;
            result.fRf = fFac;
        case 4
            result.nModelChoice = 4;
            result.fdM = fdM_rate;
            result.fRf = fdM_Fac;
%         case 6
%             result.nModelChoice = 6;
%             result.fdM = fdM_st;
%             result.fdS = fStretch;
%         case 7
%             result.nModelChoice = 7;
%             result.fdS = fdS_rate;
%             result.fRf = fdS_Fac;
%         case 8
%             result.nModelChoice = 8;
%             result.fdM = fdM_all;
%             result.fdS = fdS_all;
%             result.fRf = fFac_all;
        otherwise
            disp('Something is equal');
        end; % END of Switch result.Type
    end;% END of IF result.Type
end; % END of IF bSeismodel
end % End of IF bDecluster


function p_data = p01_data ( dim_num, data_num )

%*****************************************************************************80
%
%% P01_DATA returns the data for problem p01.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 August 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer DIM_NUM, the spatial dimension of the dependent
%    variables.
%
%    Input, integer DATA_NUM, the number of data points.
%
%    Output, real P_DATA(DIM_NUM,DATA_NUM), the data.
%
  p_data = [ ...
    0.0, 4.0; ...
    1.0, 5.0; ...
    2.0, 6.0; ...
    4.0, 6.0; ...
    5.0, 5.0; ...
    6.0, 3.0; ...
    7.0, 1.0; ...
    8.0, 1.0; ...
    9.0, 1.0; ...
   10.0, 3.0; ...
   11.0, 4.0; ...
   12.0, 4.0; ...
   13.0, 3.0; ...
   14.0, 3.0; ...
   15.0, 4.0; ...
   16.0, 4.0; ...
   17.0, 3.0; ...
   18.0, 0.0 ]';

 return
end


function [energy3M,entropy3M,sumAvg3M,corr3M,invDiffMom3M,contrast3M,...
    clustShade3M,clustPromin3M,haralCorr3M] = textureByPatchAllOffsFromCtr(scanArray3M, nL, ...
    patchSizeV, flagv, hWait, minIntensity, maxIntensity)
% function [energy3M,entropy3M,sumAvg3M,corr3M,invDiffMom3M,contrast3M,...
%     clustShade3M,clustPromin3M,haralCorr3M] = textureByPatchAllOffsFromCtr(scanArray3M, nL, ...
%     patchSizeV, flagv, hWait, minIntensity, maxIntensity)
%
% This function calculates GLCM texture from patches w.r.t. their centers.
%
% INPUTS:
% scanArray3M - Scan matrix with NaN values for voxels that are not to be
% included in texture calculation.
% nL - Number of gray levels
% patchSizeV - 3-element vector for the patch radius. For example, [3 3 0]
% flagv - 9-element vector of booleans to toggle calculation of texture features.
% hWait - (optional) waitbar handle. Use NaN to turn off waitbar.
% minIntensity - (optional) minimum intensity for discretization.
% maxIntensity - (optional) maximum intensity for discretization.
%
% Example:
% scan3M(~mask3M) = NaN;
% flagV = zeros(1,9);
% flagV(2) = 1; % entropy only
% nL = 64;
% patchSizeV = [3 3 0];
% [~,entropy3M] = textureByPatchAllOffsFromCtr(scan3M, nL, ...
%     patchSizeV, offsetsM, flagV);
%
% APA, 11/07/2018

% Generate flags
if ~exist('flagv','var')
    flagv = ones(1,9);
elseif exist('flagv','var') && isempty(flagv)
    flagv = ones(1,9);
end

% Flag to draw waitbar
waitbarFlag = 0;
if exist('hWait','var') && ishandle(hWait)
    waitbarFlag = 1;
end

% Get indices of non-NaN voxels
calcIndM = ~isnan(scanArray3M);

slcWindow = 2 * patchSizeV(3) + 1;
rowWindow = 2 * patchSizeV(1) + 1;
colWindow = 2 * patchSizeV(2) + 1;

% Build distance matrices
numColsPad = floor(colWindow/2);
numRowsPad = floor(rowWindow/2);
numSlcsPad = floor(slcWindow/2);

% Get number of voxels per slice
[numRows, numCols, numSlices] = size(scanArray3M);
numVoxels = numRows*numCols;

% Quantize the image
if exist('minIntensity','var') && exist('maxIntensity','var')
    q = imquantize_cerr(scanArray3M,nL,minIntensity,maxIntensity);
else
    q = imquantize_cerr(scanArray3M,nL);
end

% Pad q, so that sliding window works also for the edge voxels
%scanArrayTmp3M = padarray(scanArray3M,[numRowsPad numColsPad
%numSlcsPad],NaN,'both'); % aa commented
if exist('padarray.m','file')
    q = padarray(q,[numRowsPad numColsPad numSlcsPad],NaN,'both');
else
    q = padarray_oct(q,[numRowsPad numColsPad numSlcsPad],NaN,'both');
end

nanFlag = 0; % the quantized image is always padded with NaN. Hence, always,
% nanFlag = 1.
if any(isnan(q(:)))
    nanFlag = 1;
    q(isnan(q)) = nL+1;
end
lq = nL + 1;

q = uint16(q); % q is the quantized image

% Create indices for 2D blocks
[m,n,~] = size(q);
m = uint32(m);
n = uint32(n);
colWindow = uint32(colWindow);
rowWindow = uint32(rowWindow);
slcWindow = uint32(slcWindow);

% Index calculation adapted from
% http://stackoverflow.com/questions/25449279/efficient-implementation-of-im2col-and-col2im

% Start indices for each block
start_ind = reshape(bsxfun(@plus,[1:m-rowWindow+1]',[0:n-colWindow]*m),[],1);

% Row indices
lin_row = permute(bsxfun(@plus,start_ind,[0:rowWindow-1])',[1 3 2]);

% Get linear indices based on row and col indices and get desired output
indM = reshape(bsxfun(@plus,lin_row,(0:colWindow-1)*m),rowWindow*colWindow,[]);

% Indices of last level to filter out
nanIndV = false([lq*lq,1]);
if nanFlag
    nanIndV([lq:lq:lq*lq-lq, lq*lq-lq:lq*lq]) = true;
end

% Build levels vector for mu, sig
levRowV = repmat(1:lq,[1 lq]);
levColV = repmat(1:lq,[lq 1]);
levColV = levColV(:)';

% Build list of indices for px and contrast calculation
numElems = nL*nL; % APA 7/13
indCtrstM = false(nL,numElems); % APA 7/13
indPxM = false(nL,numElems); % APA 7/13
indPxPlusYm = false(nL,numElems); % APA 7/13
for n=0:nL-1
    % indices for p(x-y), contrast
    indCtrstV = false(lq*lq,1);
    indCtrst1V = 1:lq-n;
    indCtrst2V = 1+n:lq;
    indCtrstTmpV = indCtrst1V + (indCtrst2V-1)*lq;
    indCtrstTmpV = [indCtrstTmpV indCtrst2V + (indCtrst1V-1)*lq];
    indCtrstV(indCtrstTmpV) = 1;
    indCtrstV(nanIndV) = [];
    % indCtrstC{n+1} = indCtrstV;
    indCtrstM(n+1,:) = indCtrstV;

    % indices for px
    indPxV = false(lq*lq,1);
    indPxV(lq*n+1:lq*(n+1)) = true;
    indPxV(nanIndV) = [];
    % indPxC{n+1} = indPxV;
    indPxM(n+1,:) = indPxV;

end
for n=1:2*nL
    % indices for p(x+y)
    indPxPlusYv = false(lq*lq,1);
    indPxPlusYv(levRowV + levColV == n) = 1;
    indPxPlusYv(nanIndV) = [];
    % indPxPlusYc{n} = indPxPlusYv;
    indPxPlusYm(n,:) = indPxPlusYv;
end

% Build linear indices column/row-wise for Symmetry
indRowV = zeros(1,lq*lq);
for i=1:lq
    indRowV((i-1)*lq+1:(i-1)*lq+lq) = i:lq:lq*lq;
end

% Filter out NaN levels
levRowV(nanIndV) = [];
levColV(nanIndV) = [];

% Initialize
energy3M = [];
entropy3M = [];
sumAvg3M = [];
corr3M = [];
invDiffMom3M = [];
contrast3M = [];
clustShade3M = [];
clustPromin3M = [];
haralCorr3M = [];
if flagv(1)
    energy3M = zeros([numRows, numCols, numSlices],'single');
end
if flagv(2)
    entropy3M = zeros([numRows, numCols, numSlices],'single');
end
if flagv(3)
    sumAvg3M = zeros([numRows, numCols, numSlices],'single');
end
if flagv(4)
    corr3M = zeros([numRows, numCols, numSlices],'single');
end
if flagv(5)
    invDiffMom3M = zeros([numRows, numCols, numSlices],'single');
end
if flagv(6)
    contrast3M = zeros([numRows, numCols, numSlices],'single');
end
if flagv(7)
    clustShade3M = zeros([numRows, numCols, numSlices],'single');
end
if flagv(8)
    clustPromin3M = zeros([numRows, numCols, numSlices],'single');
end
if flagv(9)
    haralCorr3M = zeros([numRows, numCols, numSlices],'single');
end

tic
% Iterate over slices. compute cooccurance for all patches per slice
for slcNum = 1:numSlices
    disp(['--- Texture Calculation for Slice # ', num2str(slcNum), ' ----'])
    if flagv(1), energyV = zeros(1,numVoxels,'single'); end
    if flagv(2), entropyV = zeros(1,numVoxels,'single'); end
    if flagv(3), sumAvgV = zeros(1,numVoxels,'single'); end
    if flagv(4), corrV = zeros(1,numVoxels,'single'); end
    if flagv(5), invDiffMomV = zeros(1,numVoxels,'single'); end
    if flagv(6), contrastV = zeros(1,numVoxels,'single'); end
    if flagv(7), clustShadeV = zeros(1,numVoxels,'single'); end
    if flagv(8), clustProminV = zeros(1,numVoxels,'single'); end
    if flagv(9), haralCorrV = zeros(1,numVoxels,'single'); end

    calcSlcIndV = calcIndM(:,:,slcNum);
    calcSlcIndV = calcSlcIndV(:);
    numCalcVoxs = sum(calcSlcIndV);
    %indSlcM = indM(:,calcSlcIndV);
    indSlcM = indM(:,calcSlcIndV);
    indCurrVox = ceil(size(indM,1)/2);
    indCurrVoxV = indSlcM(indCurrVox,:);
    %slcM = uint32(q(numRowsPad+(1:numRows),numColsPad+(1:numCols),slcNum));
    % List of Voxel numbers
    voxelNumsV = uint32(0:lq*lq:lq*lq*(numCalcVoxs-1));
    slcM = uint32(q(:,:,slcNum));
    voxelValsV = slcM(indCurrVoxV);
    slc2M = slcM(indSlcM);
    slc2M = bsxfun(@plus, slc2M, (voxelValsV-1)*lq + voxelNumsV);
    %slc2M = bsxfun(@plus,slc2M,voxelNumsV);
    cooccurPatchM = accumarray(slc2M(:) ...
        ,1, [lq*lq*numCalcVoxs,1],[],...
        [],true); % patch-wise cooccurance
    cooccurPatchM = reshape(cooccurPatchM,lq*lq,numCalcVoxs);
    cooccurPatchM = cooccurPatchM + cooccurPatchM(indRowV,:); % for symmetry
    cooccurPatchM(nanIndV,:) = [];
    cooccurPatchM = bsxfun(@rdivide,cooccurPatchM,sum(cooccurPatchM)+1e-5);
    cooccurPatchM = full(cooccurPatchM);

    % Angular Second Moment (Energy)
    if flagv(1)
        energyV(calcSlcIndV) = energyV(calcSlcIndV) + sum(cooccurPatchM.^2);
    end
    % Entropy
    if flagv(2)
        entropyV(calcSlcIndV) = entropyV(calcSlcIndV) - ...
            sum(cooccurPatchM.*log2(cooccurPatchM+1e-10));
    end

    %     % Contrast, inverse Difference Moment, sum avg
    px = indPxM * cooccurPatchM;
    if flagv(3) || flagv(5) || flagv(6)
        %pXminusY = indCtrstM * cooccurPatchM;
        %pXplusY = indPxPlusYm * cooccurPatchM;
        contrastV(calcSlcIndV) = contrastV(calcSlcIndV) + ...
            (0:nL-1).^2 * indCtrstM * cooccurPatchM;
        invDiffMomV(calcSlcIndV) = invDiffMomV(calcSlcIndV) + ...
            1./(1+(0:nL-1).^2) * indCtrstM * cooccurPatchM;
        sumAvgV(calcSlcIndV) = sumAvgV(calcSlcIndV) + ...
            (1:2*nL) * indPxPlusYm * cooccurPatchM;
    end

    % weighted pixel average (mu), weighted pixel variance (sig)
    mu = (1:nL) * px;
    sig = bsxfun(@minus,(1:nL)',mu);
    sig = sum(sig .*sig .* px, 1);

    if flagv(4) || flagv(7) || flagv(8)
        levIMinusMu = bsxfun(@minus,levRowV',mu);
        levJMinusMu = bsxfun(@minus,levColV',mu);
    end

    % Correlation
    if flagv(4)
        corrV(calcSlcIndV) = corrV(calcSlcIndV) + ...
            sum(levIMinusMu .* levJMinusMu  .* cooccurPatchM, 1) ...
            ./ (sig + 1e-10); % sig.^2 to match ITK results (ITK bug)
    end

    % Cluster Shade
    if flagv(7)
        clstrV = levIMinusMu + levJMinusMu;
        clustShadeV(calcSlcIndV) = clustShadeV(calcSlcIndV) + ...
            sum(clstrV.*clstrV.*clstrV .* cooccurPatchM, 1);
    end
    % Cluster Prominence
    if flagv(8)
        clstrV = levIMinusMu + levJMinusMu;
        clustProminV(calcSlcIndV) = clustProminV(calcSlcIndV) + ...
            sum(clstrV.*clstrV.*clstrV.*clstrV .* cooccurPatchM, 1);
    end

    % Haralick Correlation
    if flagv(9)
        % muX = mean(px,1);
        muX = 1/nL;
        % sigX = bsxfun(@minus,px,muX);
        sigX = px - muX;
        sigX = sum(sigX .*sigX, 1)/(nL);

        % % Knuth method for mean and standard deviation (like ITK)
        %              muX = px(1,:);
        %              muPrevX = muX;
        %              sigX = muX*0;
        %              for col = 2:size(px,1)
        %                  muX = muPrevX + (px(col,:) - muPrevX)/col;
        %                  sigX = sigX + (px(col,:)-muX).*(px(col,:)-muPrevX);
        %                  muPrevX = muX;
        %              end
        %              sigX = sigX/nL;

        haralCorrV(calcSlcIndV) = haralCorrV(calcSlcIndV) + ...
            (levRowV .* levColV * cooccurPatchM - ...
            muX .* muX) ./ (sigX + eps);   % (levRowV-1) .* (levColV-1) to match ITK? Bug?

    end

    if flagv(1)
        energy3M(:,:,slcNum) = reshape(energyV(:),[numRows, numCols]);
    end
    if flagv(2)
        entropy3M(:,:,slcNum) = reshape(entropyV(:),[numRows, numCols]);
    end
    if flagv(3)
        sumAvg3M(:,:,slcNum) = reshape(sumAvgV(:),[numRows, numCols]);
    end
    if flagv(4)
        corr3M(:,:,slcNum) = reshape(corrV(:),[numRows, numCols]);
    end
    if flagv(5)
        invDiffMom3M(:,:,slcNum) = reshape(invDiffMomV(:),[numRows, numCols]);
    end
    if flagv(6)
        contrast3M(:,:,slcNum) = reshape(contrastV(:),[numRows, numCols]);
    end
    if flagv(7)
        clustShade3M(:,:,slcNum) = reshape(clustShadeV(:),[numRows, numCols]);
    end
    if flagv(8)
        clustPromin3M(:,:,slcNum) = reshape(clustProminV(:),[numRows, numCols]);
    end
    if flagv(9)
        haralCorr3M(:,:,slcNum) = reshape(haralCorrV(:),[numRows, numCols]);
    end

    if waitbarFlag
        set(hWait, 'Vertices', [[0 0 slcNum/numSlices slcNum/numSlices]' [0 1 1 0]']);
        drawnow;
    end
end
toc


function [fgsegment keepFascicles] = dtiSegmentFiberWithNiftiRoi(fg, targetROIfile, targetROI2file, thresholdmm)

% Extracting streamlines terminating within a certain distance from ROIs (e.g. gray matter ROIs)
%
% INPUT:
% fg: fg structure
% targetROIfile: the name of first ROI nifti file
% targetROI2file: the name of second ROI nifti file
% thresholdmm: the distance threshold in mm; the streamlines with endpoint within the threshold will be selected.
%
% Example:
% fg = fgRead(wholebrainFG);
% targetROIfile = 'Left_V3A.nii.gz';
% targetROI2file = 'Left_hV4.nii.gz';
% thresholdmm = 4;
%  [fgsegment keepFascicles] = dtiSegmentFiberWithNiftiRoi(fg, targetROIfile, targetROI2file, thresholdmm)
% (C) Hiromasa Takemura, CiNet, 2017

if notDefined('thresholdmm')
thresholdmm = 4;
end

% Load ROIs
roi1 = niftiRead(targetROIfile);
roi2 = niftiRead(targetROI2file);

% Define threshold in image space (assuming isotropic)
% Note that the threshold is squared (we keep using squared number in subsequent process)
threshold = (thresholdmm./(roi1.pixdim(1)))^2;

% Get acpc to image transformation matrix
acpc2img = inv(roi1.qto_xyz);

% Transfer fg into image coordinate
fgImg = dtiXformFiberCoords(fg, acpc2img,'img');

%% Select fibers within a certain distance from each ROIs
fprintf('Segmenting tracts from Connectome ...\n')

streamlinenum = length(fgImg.fibers);
% Extract ACPC coordinate of tract endpoints
for kk = 1:streamlinenum
    fibercoordinate = cell2mat(fgImg.fibers(kk));
    fiberlength = size(fibercoordinate);
        firstfiberend(kk,1) = fibercoordinate(1,1);
        firstfiberend(kk,2) = fibercoordinate(2,1);
        firstfiberend(kk,3) = fibercoordinate(3,1);
        secondfiberend(kk,1) = fibercoordinate(1,fiberlength(2));
        secondfiberend(kk,2) = fibercoordinate(2,fiberlength(2));
        secondfiberend(kk,3) = fibercoordinate(3,fiberlength(2));

    clear fibercoordinate fiberlength
end

%% Extract coordinates of ROIs
[tcoords(:,1), tcoords(:,2), tcoords(:,3)]= ind2sub(size(roi1.data), find(roi1.data));
[t2coords(:,1), t2coords(:,2), t2coords(:,3)]= ind2sub(size(roi2.data), find(roi2.data));

% Chose streamlines in which one endpoint is closer to first ROI, and the other
% endpoint is close to second ROI
for kp = 1:streamlinenum
    [indices_ff(kp), bestSqDis_ff(kp)] = nearpoints(transpose(firstfiberend(kp,:)), transpose(tcoords));
    [indices_ss(kp), bestSqDis_ss(kp)] = nearpoints(transpose(secondfiberend(kp,:)),transpose(t2coords));
    [indices_fs(kp), bestSqDis_fs(kp)] = nearpoints(transpose(firstfiberend(kp,:)), transpose(t2coords));
    [indices_sf(kp), bestSqDis_sf(kp)] = nearpoints(transpose(secondfiberend(kp,:)),transpose(tcoords));
end

% Select fibers within the threshold, then create keepFascicles
% structure
fffiber = find(bestSqDis_ff <threshold);
ssfiber = find(bestSqDis_ss <threshold);
fsfiber = find(bestSqDis_fs <threshold);
sffiber = find(bestSqDis_sf <threshold);

ffssfiber  = intersect(fffiber,ssfiber); % First streamline endpoint is near first ROI, the another endpoint is near second ROI
fssffiber = intersect(fsfiber,sffiber); % First streamline endpoint is near second ROI, the another endpoint is near first ROI
bothfiber = transpose(union(transpose(ffssfiber), transpose(fssffiber),'rows'));
bothsize = size(bothfiber);

%% If the selected number of streamlines are non-zero, extract those streamlines
if bothsize(2)>0
    keepFascicles = zeros(streamlinenum,1);
    for ik = 1:length(bothfiber)
        keepFascicles(bothfiber(ik)) = 1;
    end
fgsegment = fgExtract(fg, logical(keepFascicles), 'keep');
else
fprintf('No streamlines satisfied criteria ...\n')
end
end


 function y = reale(x, arg2)
%function y = reale(x)
%function y = reale(x, tol)
%function y = reale(x, 'warn')
%function y = reale(x, 'error')
%function y = reale(x, 'report')
%function y = reale(x, 'disp')
% return real part of complex data (with error checking).
% checks that imaginary part is negligible (or warning etc. if not)
% Copyright Jeff Fessler, The University of Michigan

com = 'error';
tol = 1e-13;
if nargin > 1
	if ischar(arg2)
		com = arg2;
	elseif isnumeric(arg2)
		tol = arg2;
	end
end

if streq(com, 'disp')
	;
elseif streq(com, 'warn')
	onlywarn = 1;
elseif streq(com, 'error')
	onlywarn = 0;
elseif streq(com, 'report')
	;
else
	error(sprintf('bad argument %s', com))
end

if max(abs(x(:))) == 0
	if any(imag(x(:)) ~= 0)
		error 'max real 0, but imaginary!'
	else
		y = real(x);
		return
end
end

frac = max(abs(imag(x(:)))) / max(abs(x(:)));
if streq(com, 'report')
	printf('imaginary part %g%%', frac * 100)
	return
end

if frac > tol
	t = sprintf('%s: imaginary fraction %g', mfilename, frac);
	if streq(com, 'disp')
		disp(t)
	elseif onlywarn
		disp(t)
	else
		error(t)
	end
end
y = real(x);


function out=airdata(uu)
%
% Fake air data - this will be replaced with real air data in Chapter 4
%
% modified 12/11/2009 - RB

    % process inputs to function
    pn          = uu(1);             % North position (meters)
    pe          = uu(2);             % East position (meters)
    h           = -uu(3);            % altitude (meters)
    u           = uu(4);             % body velocity along x-axis (meters/s)
    v           = uu(5);             % body velocity along y-axis (meters/s)
    w           = uu(6);             % body velocity along z-axis (meters/s)
    phi         = 180/pi*uu(7);      % roll angle (degrees)
    theta       = 180/pi*uu(8);      % pitch angle (degrees)
    psi         = 180/pi*uu(9);      % yaw angle (degrees)
    p           = 180/pi*uu(10);     % body angular rate along x-axis (degrees/s)
    q           = 180/pi*uu(11);     % body angular rate along y-axis (degrees/s)
    r           = 180/pi*uu(12);     % body angular rate along z-axis (degrees/s)

    Va = sqrt(u^2+v^2+w^2);
    alpha = atan2(w,u);
    beta  = asin(v);
    wn    = 0;
    we    = 0;
    wd    = 0;

    out = [Va; alpha; beta; wn; we; wd];


function h = rdivide(f, g, pref)
%./   Pointwise CHEBFUN right divide.
%   F./G returns a CHEBFUN that represents the function F(x)/G(x).
%   If F and G are array-valued column (row) CHEBFUNs, they must have the same
%   number of columns (rows).
%
% See also MRDIVIDE, TIMES.

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

% Trivial empty case:
if ( isempty(f) || isempty(g) )
    h = chebfun();
    return
end

% Trivial zero numerator case:
if ( isnumeric(f) && ~any(f) )
    h = 0*g;
    return
end

if ( nargin < 3 )
    pref = chebfunpref();
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CHEBFUN ./ CHEBFUN %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if ( isa(f,'chebfun') && isa(g, 'chebfun') )

    % Check the number of columns match:
    if ( numColumns(f) ~= numColumns(g) )
        if ( numColumns(f) == 1 )
            h = g;
            for k = 1:numColumns(g)
                h(k) = rdivide(f, g(k));
            end
        elseif ( numColumns(g) == 1 )
            h = f;
            for k = 1:numColumns(f)
                h(k) = rdivide(f(k), g);
            end
        else
            error('CHEBFUN:CHEBFUN:rdivide:quasi', ...
                'Chebfun quasimatrix dimensions must agree.')
        end
        return
    end

    if ( numel(f) == 1 && numel(g) == 1 )
        % CHEBFUN case :

        % If one of the two CHEBFUNs uses a PERIODICTECH reprensetation,
        % cast it to a NONPERIODICTECH.
        if ( ~isPeriodicTech(f.funs{1}) && isPeriodicTech(g.funs{1}) )
            g = chebfun(g, g.domain, 'tech', get(f.funs{1}, 'tech'));
        elseif ( isPeriodicTech(f.funs{1}) && ~isPeriodicTech(g.funs{1}) )
            f = chebfun(f, f.domain, 'tech', get(g.funs{1}, 'tech'));
        end

        % Array-valued CHEBFUN case:
        h = columnRdivide(f, g, pref);
    else
        % QUASIMATRIX case:

        % Convert to a cell array:
        f = mat2cell(f);
        g = mat2cell(g);
        % Loop over the columns:
        for k = numel(f):-1:1
            h(k) = columnRdivide(f{k}, g{k}, pref);
        end
    end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CHEBFUN ./ constant %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
elseif ( isa(f, 'chebfun') )

    numColsF = numColumns(f);
    numelF = numel(f);
    numelG = numel(g);

    % Different cases:
    if ( numColsF == 1 )
        % e.g., x./[1 2 3]
        h = columnRdivide(f, g, pref);
    elseif ( numelG == 1 )
        % e.g., [x sin(x)]./2
        for k = numelF:-1:1
            h(k) = columnRdivide(f(k), g, pref);
        end
    elseif ( numelG == numColsF )
        % e.g., [x sin(x) exp(x)]./[1 2 3]
        if ( numel(f) == 1 )
            h = columnRdivide(f, g, pref);
        else
            f = mat2cell(f);
            for k = numColsF:-1:1
                h(k) = columnRdivide(f{k}, g(k), pref);
            end
        end
    else
        error('CHEBFUN:CHEBFUN:rdivide:dim', ...
            'Chebfun quasimatrix dimensions must agree.');
    end

%%%%%%%%%%%%%%%%%%%%%%%%%%%% constant ./ CHEBFUN %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
else

    numColsG = numColumns(g);
    numelF = numel(f);
    numelG = numel(g);

    % Different cases:
    if ( numColsG == 1 )
        % e.g., [1 2 3]./(2+x)
        for k = numelF:-1:1
            h(k) = columnRdivide(f(k), g, pref);
        end
        h = quasi2cheb(h);
    elseif ( numelF == 1 )
        % e.g., 2./(2+[x x abs(x)])
        g = mat2cell(g);
        for k = numColsG:-1:1
            h(k) = columnRdivide(f, g{k}, pref);
        end
        try h = quasi2cheb(h); catch, end
    elseif ( numelF == numColsG )
        % e.g., [1 2]./(2+[x sin(x)])
        g = mat2cell(g);
        for k = numColsG:-1:1
            h(k) = columnRdivide(f(k), g{k}, pref);
        end
        try h = quasi2cheb(h); catch, end
    else
        error('CHEBFUN:CHEBFUN:rdivide:dim', ...
            'Chebfun quasimatrix dimensions must agree.');
    end

end

end

function h = columnRdivide(f, g, pref)

% If g is numeric then call TIMES():
if ( isnumeric(g) )
    if ( any(g == 0) )
        % note g could be a vector, with some zero components
        % TODO:  Return identically Inf/NaN CHEBFUN instead?
        error('CHEBFUN:CHEBFUN:rdivide:columnRdivide:divisionByZero', ...
            'Division by zero.')
    end
    h = f.*(1./g);
    return
end

% Check for zero FUNs:
for k = 1:numel(g.funs)
    if ( iszero(g.funs{k}) )
        % TODO:  Return CHEBFUN with identically Inf/NaN FUN instead?
        error('CHEBFUN:CHEBFUN:rdivide:columnRdivide:divisionByZeroChebfun', ...
            'Division by CHEBFUN with identically zero FUN.');
    end
end

% Add breaks at the roots of g:
g = addBreaksAtRoots(g, pref);

if ( isa(f, 'chebfun') )

    % Check that the domains are the same:
    if ( ~domainCheck(f, g) )
        error('CHEBFUN:CHEBFUN:rdivide:columnRdivide:domain', ...
            'Inconsistent domains.');
    end

    % Check that the orientation is the same:
    if ( xor(f.isTransposed, g.isTransposed) )
        error('CHEBFUN:CHEBFUN:rdivide:columnRdivide:dim', ...
            'Matrix dimension do not agree (transposed)');
    end

    % Introduce matching breakpoints in f and g:
    [f, g] = overlap(f, g);

    % Copy g to h in preparation for output:
    h = g;

    % Loop over the FUNS:
    for k = 1:numel(g.funs)
        h.funs{k} = rdivide(f.funs{k}, g.funs{k});
    end

    % Divide the pointValues:
    h.pointValues = f.pointValues./g.pointValues;

    % Avoid NaN or Inf from divide by zero where possible:
    hpv = h.pointValues; fpv = f.pointValues; gpv = g.pointValues;
    idx = (isnan(hpv) & ~(isnan(fpv) | isnan(gpv))) | ...
        (isinf(hpv) & (abs(fpv) < 100*chebfuneps*vscale(f)));
    if ( any(idx) )
        pvavg = (feval(h, h.domain, 'left') + feval(h, h.domain, 'right'))/2;
        h.pointValues(idx) = pvavg(idx);
    end

else

    % Copy g to h in preparation for output:
    h = g;

    % Loop over the FUNS:
    for k = 1:numel(g.funs)
        h.funs{k} = rdivide(f, g.funs{k});
    end

    % Divide the pointValues:
    h.pointValues = f./g.pointValues;

end

end


function femI = genP1IFEM3D(mesh,fem,btM,btP)
%% Usage: Generate Quadrature Information on Interface Elements
%         Each interface tetrahedron is cut into 4 to 9 small tetrahedra
%
% femI.t --- global index on small tetrahedra
% femI.bas --- nt-4-4 matrix stores basis functions on all small tetra
% femI.gx --- Gaussian x nodes
% femI.gy --- Gaussian y nodes
% femI.gz --- Gaussian z nodes
% femI.gw --- Gaussian weights
% femI.area --- Areas of all small tetra
% femI.quadT --- number of small tetras of each interface element
% femI.locID --- order of local index, to match with the basis function

% ***** The following info only used for partial penalty IFEM *****

% femI.bas1 --- nti-4-4 basis function on 1st piece of interface element
% femI.bas2 --- nti-4-4 basis function on 2nd piece of interface element
% femI.plusPC --- 1 or 2 denotes which piece corresponds to btP.

% Last Modified by Xu Zhang 08/02/2020

%%
ntI = -min(mesh.tLoc);
t = zeros(8*ntI,4); bas = zeros(8*ntI,4,4); A = zeros(8*ntI,1);
gx = zeros(8*ntI,4); gy = zeros(8*ntI,4); gz = zeros(8*ntI,4);
QelemID = zeros(8*ntI,1);
quadT = zeros(ntI,1); locIDvec = zeros(ntI,4);
bas1 = zeros(ntI,4,4); bas2 = zeros(ntI,4,4); plusPC = zeros(ntI,1);
intID = find(mesh.tLoc<0);
id = 0;
for i = 1:ntI
    tID = intID(i);
    t_e = mesh.t_e(tID,:);
    intpt = mesh.eIntP(-mesh.eLoc(t_e(mesh.eLoc(t_e)<0)),:);
    pLocK = mesh.pLoc(mesh.t(tID,:));
    vert0 = mesh.p(mesh.t(tID,:),:);
    if size(intpt,1) == 3 % Type 1 Interface Element (3 intersection pts)
        if pLocK(1) == -1 && pLocK(2) == 1 && pLocK(3) == 1 && pLocK(4) == 1
            locID = [1,2,3,4]; vert = vert0(locID,:); coef = [btM,btP]; plusPC(i) = 2;
        elseif pLocK(1) == 1 && pLocK(2) == -1 && pLocK(3) == 1 && pLocK(4) == 1
            locID = [2,3,4,1]; vert = vert0(locID,:); coef = [btM,btP]; plusPC(i) = 2;
        elseif pLocK(1) == 1 && pLocK(2) == 1 && pLocK(3) == -1 && pLocK(4) == 1
            locID = [3,4,1,2]; vert = vert0(locID,:); coef = [btM,btP]; plusPC(i) = 2;
        elseif pLocK(1) == 1 && pLocK(2) == 1 && pLocK(3) == 1 && pLocK(4) == -1
            locID = [4,1,2,3]; vert = vert0(locID,:); coef = [btM,btP]; plusPC(i) = 2;
        elseif pLocK(1) == 1 && pLocK(2) == -1 && pLocK(3) == -1 && pLocK(4) == -1
            locID = [1,2,3,4]; vert = vert0(locID,:); coef = [btP,btM]; plusPC(i) = 1;
        elseif pLocK(1) == -1 && pLocK(2) == 1 && pLocK(3) == -1 && pLocK(4) == -1
            locID = [2,3,4,1]; vert = vert0(locID,:); coef = [btP,btM]; plusPC(i) = 1;
        elseif pLocK(1) == -1 && pLocK(2) == -1 && pLocK(3) == 1 && pLocK(4) == -1
            locID = [3,4,1,2]; vert = vert0(locID,:); coef = [btP,btM]; plusPC(i) = 1;
        elseif pLocK(1) == -1 && pLocK(2) == -1 && pLocK(3) == -1 && pLocK(4) == 1
            locID = [4,1,2,3]; vert = vert0(locID,:); coef = [btP,btM]; plusPC(i) = 1;
        end
        p1 = [vert(1,:);intpt]; t1 = delaunay(p1);
        p2 = [vert(2:4,:);intpt];t2 = delaunay(p2);
    elseif size(intpt,1) == 4 % Type 2 Interface Element (4 intersection pts)
        if pLocK(1) == -1 && pLocK(2) == -1 && pLocK(3) == 1 && pLocK(4) == 1
            locID = [1,2,3,4]; vert = vert0(locID,:); coef = [btM,btP]; plusPC(i) = 2;
        elseif pLocK(1) == -1 && pLocK(2) == 1 && pLocK(3) == -1 && pLocK(4) == 1
            locID = [1,3,2,4]; vert = vert0(locID,:); coef = [btM,btP]; plusPC(i) = 2;
        elseif pLocK(1) == -1 && pLocK(2) == 1 && pLocK(3) == 1 && pLocK(4) == -1
            locID = [1,4,2,3]; vert = vert0(locID,:); coef = [btM,btP]; plusPC(i) = 2;
        elseif pLocK(1) == 1 && pLocK(2) == -1 && pLocK(3) == -1 && pLocK(4) == 1
            locID = [1,4,2,3]; vert = vert0(locID,:); coef = [btP,btM]; plusPC(i) = 1;
        elseif pLocK(1) == 1 && pLocK(2) == -1 && pLocK(3) == 1 && pLocK(4) == -1
            locID = [1,3,2,4]; vert = vert0(locID,:); coef = [btP,btM]; plusPC(i) = 1;
        elseif pLocK(1) == 1 && pLocK(2) == 1 && pLocK(3) == -1 && pLocK(4) == -1
            locID = [1,2,3,4]; vert = vert0(locID,:); coef = [btP,btM]; plusPC(i) = 1;
        end
        p1 = [vert(1:2,:);intpt]; t1 = delaunay(p1);
        p2 = [vert(3:4,:);intpt]; t2 = delaunay(p2);
    else
        stp = 1;
    end
    [BAS1,BAS2] = basIFE3DP1coef(vert,intpt,coef);
    X1 = p1(t1(:,1),:); X2 = p1(t1(:,2),:); X3 = p1(t1(:,3),:); X4 = p1(t1(:,4),:);
    [gx1, gy1, gz1] = gaussPtetra(X1,X2,X3,X4,fem.ng);
    A1 = tetraArea(X1,X2,X3,X4);

    X1 = p2(t2(:,1),:); X2 = p2(t2(:,2),:); X3 = p2(t2(:,3),:); X4 = p2(t2(:,4),:);
    [gx2, gy2, gz2] = gaussPtetra(X1,X2,X3,X4,fem.ng);
    A2 = tetraArea(X1,X2,X3,X4);

    nt1 = size(t1,1); nt2 = size(t2,1);
    bas(id+1:id+nt1,:,1) = repmat(BAS1(1,:),nt1,1);
    bas(id+1:id+nt1,:,2) = repmat(BAS1(2,:),nt1,1);
    bas(id+1:id+nt1,:,3) = repmat(BAS1(3,:),nt1,1);
    bas(id+1:id+nt1,:,4) = repmat(BAS1(4,:),nt1,1);
    bas(id+nt1+1:id+nt1+nt2,:,1) = repmat(BAS2(1,:),nt2,1);
    bas(id+nt1+1:id+nt1+nt2,:,2) = repmat(BAS2(2,:),nt2,1);
    bas(id+nt1+1:id+nt1+nt2,:,3) = repmat(BAS2(3,:),nt2,1);
    bas(id+nt1+1:id+nt1+nt2,:,4) = repmat(BAS2(4,:),nt2,1);
    gx(id+1:id+nt1+nt2,:) = [gx1;gx2];
    gy(id+1:id+nt1+nt2,:) = [gy1;gy2];
    gz(id+1:id+nt1+nt2,:) = [gz1;gz2];
    A(id+1:id+nt1+nt2,:) = [A1;A2];
    QelemID(id+1:id+nt1+nt2) = tID;

    temp = fem.t(tID,:);
    temp2 = temp(locID);
    t(id+1:id+nt1+nt2,:) = repmat(temp2,nt1+nt2,1);
    locIDvec(i,:) = locID;
    quadT(i) = nt1+nt2;

    bas1(i,:,1) = BAS1(1,:); bas2(i,:,1) = BAS2(1,:);
    bas1(i,:,2) = BAS1(2,:); bas2(i,:,2) = BAS2(2,:);
    bas1(i,:,3) = BAS1(3,:); bas2(i,:,3) = BAS2(3,:);
    bas1(i,:,4) = BAS1(4,:); bas2(i,:,4) = BAS2(4,:);

    id = id+nt1+nt2;
end
t(id+1:end,:) = []; bas(id+1:end,:,:) = []; A(id+1:end) = [];
gx(id+1:end,:) = []; gy(id+1:end,:) = []; gz(id+1:end,:) = [];
QelemID(id+1:end,:) = [];
femI = struct('t',t,'bas',bas,'gx',gx,'gy',gy,'gz',gz,'area',A,'gw',fem.gw,...
    'quadT',quadT,'locID',locIDvec,'bas1',bas1,'bas2',bas2,'plusPC',plusPC,...
    'QelemID',QelemID);


function [It, Ix, Iy] = partial_deriv(images, uv_prev, interpolation_method, deriv_filter, b)

%PARTIAL_DERIV   Spatio-temporal derivatives
%   P = PARTIAL_DERIV(IMAGES, INIT) computes the spatio-temporal derivatives
%
%   Author: Deqing Sun, Department of Computer Science, Brown University
%   Contact: dqsun@cs.brown.edu
%   $Date: 2007-11-30 $
%   $Revision $
%
% Copyright 2007-2010, Brown University, Providence, RI. USA
%
%                          All Rights Reserved
%
% All commercial use of this software, whether direct or indirect, is
% strictly prohibited including, without limitation, incorporation into in
% a commercial product, use in a commercial service, or production of other
% artifacts for commercial purposes.
%
% Permission to use, copy, modify, and distribute this software and its
% documentation for research purposes is hereby granted without fee,
% provided that the above copyright notice appears in all copies and that
% both that copyright notice and this permission notice appear in
% supporting documentation, and that the name of the author and Brown
% University not be used in advertising or publicity pertaining to
% distribution of the software without specific, written prior permission.
%
% For commercial uses contact the Technology Venture Office of Brown University
%
% THE AUTHOR AND BROWN UNIVERSITY DISCLAIM ALL WARRANTIES WITH REGARD TO
% THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
% FITNESS FOR ANY PARTICULAR PURPOSE.  IN NO EVENT SHALL THE AUTHOR OR
% BROWN UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
% DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
% PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
% ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
% THIS SOFTWARE.

if nargin == 2
    interpolation_method = 'cubic';
end;

if nargin == 4
    h = deriv_filter;
else
    h = [1 -8 0 8 -1]/12; % used in Wedel etal "improved TV L1"
end;

if nargin < 5
    b = 0.5;    %blending ratio
end;

H   = size(images, 1);
W   = size(images, 2);

[x,y]   = meshgrid(1:W,1:H);
x2      = x + uv_prev(:,:,1);
y2      = y + uv_prev(:,:,2);

% Record out of boundary pixels
B = (x2>W) | (x2<1) | (y2>H) | (y2<1);

if size(images, 4) ~= 1
    B = repmat(B, [1 1 size(images, 3)]);
end;

if size(images, 4) == 1
    img1 = images(:,:,1);
    img2 = images(:,:,2);
else
    img1 = images(:,:,:,1);
    img2 = images(:,:,:,2);
end;

if strcmp(interpolation_method, 'bi-cubic')

    %h = [-0.5, 0, 0.5]; % consistent with bi-cubic interpolation

    % Bicubic interpolation
    if nargout == 1

        if size(images, 4) == 1
            % gray-level
            warpIm = interp2_bicubic(images(:,:,2),x2,y2, h);
        else
            % color
            warpIm  = zeros(size(images(:,:,:,1)));
            for j = 1:size(images,3)
                warpIm(:,:,j) = interp2_bicubic(images(:,:,j, 2),x2,y2, h);
            end;
        end;

    elseif nargout == 3

        if size(images, 4) == 1
            % gray-level
            [warpIm Ix Iy] = interp2_bicubic(images(:,:,2),x2,y2, h);
        else
            % color
            warpIm  = zeros(size(images(:,:,:,1)));
            Ix      = warpIm;
            Iy      = warpIm;
            for j = 1:size(images,3)
                [warpIm(:,:,j) Ix(:,:,j) Iy(:,:,j)] = interp2_bicubic(images(:,:,j,2),x2,y2, h);
            end;
        end;

    else
        error('partial_deriv: number of output wrong!');
    end;

    indx        = isnan(warpIm);
    if size(images, 4) == 1
        It          = warpIm - images(:,:,1);
    else
        It          = warpIm - images(:,:,:,1);
    end;

    % Disable those out-of-boundary pixels in warping
    It(indx)    = 0;
    if nargout == 3

        % Temporal average
        I1x = imfilter(img1, h,  'corr', 'symmetric', 'same');  %
        I1y = imfilter(img1, h', 'corr', 'symmetric', 'same');

        % b = 0.6;    % recommended in Wedal etal "improved TV-L1" 2008
        % b = 0.5;
%         b = 1;

        Ix  = b*Ix+(1-b)*I1x;
        Iy  = b*Iy+(1-b)*I1y;

        Ix(indx) = 0;
        Iy(indx) = 0;
    end;

elseif strcmp(interpolation_method, 'bi-linear') || strcmp(interpolation_method, 'cubic')

    if strcmp(interpolation_method, 'bi-linear')
        method = 'linear';
    else
        method = 'cubic';
    end;

    % Matlab built-in
    if size(images, 4) == 1
        % Gray-level
        warpIm  = interp2(x,y,images(:,:,2),x2,y2,method);
        It      = warpIm - images(:,:,1);

    else
        % Color
        warpIm  = zeros(size(images(:,:,:,1)));
        for j = 1:size(images,3)
            warpIm(:,:,j) = interp2(x,y,images(:,:,j,2),x2,y2,method, NaN);
        end;
        It      = warpIm - images(:,:,:,1);

    end;

        % spline-based bicubic interpolation code with incorrect derivatives (below)
%         if size(images, 4) == 1
%             % gray-level
%             warpIm = interp2_bicubic(images(:,:,2),x2,y2, h);
%             It      = warpIm - images(:,:,1);
%         else
%             % color
%             warpIm  = zeros(size(images(:,:,:,1)));
%             for j = 1:size(images,3)
%                 warpIm(:,:,j) = interp2_bicubic(images(:,:,j, 2),x2,y2, h);
%             end;
%             It      = warpIm - images(:,:,:,1);
%         end;

    if nargout == 3

        % First compute derivative then warp
        I2x = imfilter(img2, h,  'corr', 'symmetric', 'same');
        I2y = imfilter(img2, h', 'corr', 'symmetric', 'same');

        if size(images, 4) == 1
            % Gray-level
            Ix  = interp2(x,y,I2x,x2,y2,method);
            Iy  = interp2(x,y,I2y,x2,y2,method);

%             % spline-based bicubic interpolation code with incorrect derivatives
%             Ix  = interp2_bicubic(I2x,x2,y2, h);
%             Iy  = interp2_bicubic(I2y,x2,y2, h);

        else
            % Color
            Ix  = zeros(size(images(:,:,:,1)));
            Iy  = Ix;

            for j = 1:size(images,3)
                Ix(:,:,j)  = interp2(x,y,I2x(:,:,j),x2,y2,method);
                Iy(:,:,j)  = interp2(x,y,I2y(:,:,j),x2,y2,method);
            end;

        end;

        % warp then derivative
%         if size(images, 4) == 1
%             tmp           = images(:,:,1);
%         else
%             tmp           = images(:,:,:,1);
%         end;
%         warpIm(B) = tmp(B);
%         Ix        = imfilter(warpIm, h,  'corr', 'symmetric', 'same');  %
%         Iy        = imfilter(warpIm, h', 'corr', 'symmetric', 'same');
        % end of warp then derivative

        % Temporal average
        I1x = imfilter(img1, h,  'corr', 'symmetric', 'same');  %
        I1y = imfilter(img1, h', 'corr', 'symmetric', 'same');

        % b = 0.6;    % recommended in Wedal etal "improved TV-L1" 2008
        % b = 0.5;

        Ix  = b*Ix+(1-b)*I1x;
        Iy  = b*Iy+(1-b)*I1y;

    end;

    % Disable those out-of-boundary pixels in warping
    It(B)   = 0;
    if nargout == 3
        Ix(B) = 0;
        Iy(B) = 0;
    end;

else
    error('partial_deriv: unknown interpolation method!');
end;