function [H,dH] = entub_vbmc(vp,grad_flags,jacobian_flag)
%ENTUB_VBMC Entropy upper bound for variational posterior

% Uses entropy upper bound of multivariate normal approximation

% Check if gradient computation is required
if nargout < 2                              % No 2nd output, no gradients
    grad_flags = false;
elseif nargin < 2 || isempty(grad_flags)    % By default compute all gradients
    grad_flags = true;
end
if isscalar(grad_flags); grad_flags = ones(1,4)*grad_flags; end

% By default assume variational parameters were transformed (before the call)
if nargin < 3 || isempty(jacobian_flag); jacobian_flag = true; end

D = vp.D;           % Number of dimensions
K = vp.K;           % Number of components
mu(:,:) = vp.mu;
sigma(1,:) = vp.sigma;
lambda(:,1) = vp.lambda(:);
w(1,:) = vp.w;

% Check which gradients are computed
if grad_flags(1); mu_grad = zeros(D,K); dS_mu = zeros(D,D,K); else, mu_grad = []; end
if grad_flags(2); sigma_grad = zeros(K,1); else, sigma_grad = []; end
if grad_flags(3); lambda_grad = zeros(D,1); else, lambda_grad = []; end
if grad_flags(4); w_grad = zeros(K,1); dS_w = zeros(D,D,K); else, w_grad = []; end

if K == 1
    % Entropy of single component, uses exact expression
    H = 0.5*D*(1 + log(2*pi)) + D*sum(log(sigma)) + sum(log(lambda));

    if grad_flags(2)
        sigma_grad(:) = D./sigma(:);
    end

    if grad_flags(3)
        % Should be dividing by LAMBDA, see below
        lambda_grad(:) = ones(D,1); % 1./lambda(:);
    end

    if grad_flags(4)
        w_grad = 0;
    end
else

    Mu = sum(bsxfun(@times,vp.w,vp.mu),2);
    Sigma = zeros(D,D);
    delta_mu = bsxfun(@minus,mu,Mu);
    for k = 1:K
        S_k = diag((lambda*sigma(k)).^2) + delta_mu(:,k)*delta_mu(:,k)';
        Sigma = Sigma + w(k)*S_k;
        if grad_flags(4); dS_w(:,:,k) = S_k; end
    end
    L = chol(Sigma);

    H = 0.5*D*(log(2*pi) + 1) + sum(log(diag(L)));

     if any(grad_flags)
         invK = L\(L'\eye(D));

         if grad_flags(1)
             for k = 1:K
                 mu_grad((1:D)+(k-1)*D) = 0.5*w(k).*(sum(bsxfun(@times,invK,delta_mu(:,k)'),2) + sum(bsxfun(@times,invK,delta_mu(:,k)),1)');
             end
         end

         if grad_flags(2)
             Q = sum(sum(invK.*diag(lambda.^2)));
             sigma_grad(:) = Q*(w.*sigma);
         end

         if grad_flags(3)
             lambda_grad(:) = diag(invK).*lambda.^2*sum(w.*(sigma.^2));
         end

         if grad_flags(4)
             for k = 1:K
                w_grad(k) = 0.5*sum(sum(invK.*dS_w(:,:,k)));
             end
         end
     end
end

if nargout > 1
    % Correct for standard log reparameterization of SIGMA
    if jacobian_flag && grad_flags(2)
        sigma_grad = bsxfun(@times,sigma_grad, sigma(:));
    end
    % Correct if NOT using standard log reparameterization of LAMBDA
    if ~jacobian_flag && grad_flags(3)
        lambda_grad = bsxfun(@rdivide,lambda_grad, lambda(:));
    end
    % Correct for standard softmax reparameterization of W
    if jacobian_flag && grad_flags(4)
        eta_sum = sum(exp(vp.eta));
        J_w = bsxfun(@times,-exp(vp.eta)',exp(vp.eta)/eta_sum^2) + diag(exp(vp.eta)/eta_sum);
        w_grad = J_w*w_grad;
    end
    dH = [mu_grad(:); sigma_grad(:); lambda_grad(:); w_grad(:)];
end

end


function value = mono_422_3d ( n, x )

%*****************************************************************************80
%
%% MONO_422_3D evaluates X**4 Y**2 Z**2.
%
%  Modified:
%
%    25 May 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the spatial dimension.
%
%    Input, real X(N), the point at which the monomial is to be evaluated.
%
%    Output, real VALUE, the value of the monomial.
%
  value = x(1)^4 * x(2)^2 * x(3)^2;

  return
end


function H = sims_bfgsi(H0,dg,dx)
% H = bfgsi(H0,dg,dx)
% dg is previous change in gradient; dx is previous change in x;
% 6/8/93 version that updates inverse hessian instead of hessian
% itself.
% Copyright by Christopher Sims 1996.  This material may be freely
% reproduced and modified.
if size(dg,2)>1
   dg=dg';
end
if size(dx,2)>1
   dx=dx';
end
Hdg = H0*dg;
dgdx = dg'*dx;
if (abs(dgdx) >1e-12)
   H = H0 + (1+(dg'*Hdg)/dgdx)*(dx*dx')/dgdx - (dx*Hdg'+Hdg*dx')/dgdx;
else

   H=H0;
end
save H.dat H


function [scimat, sigma] = scimat_estimate_bias_field(scimat, x, a, sigma, FASTINTERP)
% SCIMAT_ESTIMATE_BIAS_FIELD  Estimate MRI bias field.
%
%   This function provides an estimate of the bias field from a magnetic
%   resonance image (MRI).
%
%   The function samples a SCIMAT image in locations provided by the
%   user (typically, corresponding to background voxels), and then creates
%   another image that interpolates the sampled intensity values.
%
%   The locations can be selected e.g. using our Spline Tool extension to
%   the Seg3D platform (click the points, and then export the control
%   points).
%
%   Note that the interpolant is an interpolating thin-plate spline (TPS),
%   but instead of just sampling the selected voxels, a neighbourhood is
%   sampled, and used to locally low-pass filter the image with a Gaussian
%   filter. Thus, this function is somehow robust in the presence of noise.
%   Alternatively, this function could use an approximating TPS instead of
%   an interpolating one.
%
% SCIMAT2 = scimat_estimate_bias_field(SCIMAT, X)
%
%   SCIMAT is an image (see "help scimat" for details).
%
%   X is a 3-colum matrix with the real world coordinates of the sampling
%   points. Note that each point is rounded to the closest voxel centre.
%
%   SCIMAT2 is the estimated bias field in SCIMAT format.
%
% SCIMAT2 = scimat_estimate_bias_field(SCIMAT, X, A, SIGMA, FASTINTERP)
%
%   A is a scaling factor. Because using the TPS to interpolate all voxels
%   can be rather slow, and the bias field is anyway a slow varying field,
%   it's convenient to first quickly reduce the image size by A (using
%   bilinear interpolation), interpolate the bias field in the smaller
%   image with the TPS, and then expand to the original size. By default, A
%   = 1.0 and no rescaling is used. If A has 1 value, then the same scaling
%   is applied to all dimensions. If A has 3 values, each value is used to
%   scale one dimension.
%
%   Note: Small values of A imply a large SIGMA to avoid aliasing.
%   Internally, this attempts to create a large Gaussian filter, and the
%   computer may run out of memory. Larger values of A require a lot of
%   memory from the thin-plate spline, and this may give an out of memory
%   error too.
%
%   SIGMA is the standard deviation of the Gaussian filter. By default,
%   sigma = 2*sqrt(-2 ln(alpha)) / a, alpha=1/sqrt(2)*ones(1,3), so that
%   the Gaussian filter has a 3dB drop at the cut-off frequency in each
%   dimension (see Note 1 for details).
%
%   FASTINTERP is a boolean to decide whether fast (more memory consuming)
%   thin-plate spline interpolation should be used. By default,
%   FASTINTERP=true. If you run out of memory, try setting it to false.
%
% [SCIMAT2, SIGMA] = ...
%
%   SIGMA as an output is the sigma value (in voxel units) used for the
%   Gaussian filter.
%
% ======
% Note 1
% ======
%
% The Gaussian filter in the frequency domain is
%
%   F(u) = 1/sqrt(2*pi) * exp(-u^2 *sigma^2 / 2)
%
% So if we want our filter to have a drop alpha at cut-off frequency B'
%
%   exp(-B'^2 * sigma^2 / 2) = alpha
%
% then
%
%   sigma = sqrt(-2 ln(alpha)) / B'
%
% Because of the Nyquist theorem, in order to prevent aliasing, we require
% the cut-off frequency to be
%
%   B' < fs' / 2 = a / (2*dx)
%
% then the standard deviation for the Gaussian must be
%
%   sigma > 2*sqrt(-2 ln(alpha)) / fs
%
%   sigma > 2*sqrt(-2 ln(alpha)) * dx / a
%
% There are two free parameters:
%
%   * alpha: filter drop at cut-off frequency (alpha=1/sqrt(2) makes a 3 dB
%            cut-off point)
%
%   * a: scaling factor for the image (a=.5, image becomes half size)
%
%  and a parameter that depends on the image
%
%   * dx: voxel size, image resolution; because sigma is given in pixel
%     units, dx = 1 for any image.
%
% ======
% Note 2
% ======
%
% The Gaussian filter in the space domain is
%
%   f(x) = 1/(sqrt(2*pi)*sigma) * exp(-x^2 / (2*sigma^2))
%
% The Gaussian tails tend asymptotically to 0, so we need to truncate the
% filter at some point. For sigma = 1.0, the filter tail at
% f(4)=1.3383e-04, which is a good compromise between having a small
% numerical error and a small filter.

% Author: Ramon Casero <rcasero@gmail.com>
% Copyright © 2011-2014 University of Oxford
% Version: 0.2.1
%
% University of Oxford means the Chancellor, Masters and Scholars of
% the University of Oxford, having an administrative office at
% Wellington Square, Oxford OX1 2JD, UK.
%
% This file is part of Gerardus.
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% 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. The offer of this
% program under the terms of the License is subject to the License
% being interpreted in accordance with English Law and subject to any
% action against the University of Oxford being under the jurisdiction
% of the English Courts.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.

% check arguments
narginchk(2, 5);
nargoutchk(0, 2);

% defaults
alpha = 1/sqrt(2)*ones(1,3);
if (nargin < 3 || isempty(a))
    a = 1.0;
end
if (length(a) == 1)
    a = a([1 1 1]);
elseif (length(a) ~= 3)
    error('A must have 1 or 3 values')
end
if (nargin < 4 || isempty(sigma))
    sigma = 2*sqrt(-2*log(alpha)) ./ a;
end
if (nargin < 5 || isempty(FASTINTERP))
    FASTINTERP = true;
end

% squeeze volume
scimat = scimat_squeeze(scimat);

% size of input volume
nin = [scimat.axis.size];

% recompute the scaling factor so that we obtain an integer number of
% voxels in the output volume
nout = round(nin .* a);
a = nout ./ nin;

% Seg3D provides continuous point coordinates over the whole image volume.
% But in fact, intensity values correspond to the voxel centre, so we have
% to convert the coordinates to indices and round them
x = scimat_world2index(x, scimat);
x = round(x);

% compute low-pass anti-aliasing filter
% sigma = 2 * sqrt(-2 * log(alpha)) ./ a .* dx; % dx not needed, sigma
% given in pixel units
halfsz = round(4 * sigma);
h = fspecial3('gaussian', halfsz*2+1, sigma);

% loop each sampling point. For each sampling point we want to sample a
% whole cube around it, so that we can apply a low-pass filter without
% having to filter the whole image volume
v = zeros(size(x, 1), 1);
for I = 1:size(x, 1)

    % get indices of the area around the sampling point to sample. If the
    % sampling point is too close to the edge, we have to be careful to not
    % overflow
    idxr = max(1, x(I, 1)-halfsz(1)):min(nin(1), x(I, 1)+halfsz(1));
    idxc = max(1, x(I, 2)-halfsz(2)):min(nin(2), x(I, 2)+halfsz(2));
    idxs = max(1, x(I, 3)-halfsz(3)):min(nin(3), x(I, 3)+halfsz(3));

    % if necessary, crop low-pass filter so that it has the same size as
    % the sampling area
    hbox = h(...
        max(1, idxr - x(I, 1) + halfsz(1) + 1), ...
        max(1, idxc - x(I, 2) + halfsz(2) + 1), ...
        max(1, idxs - x(I, 3) + halfsz(3) + 1));

    % extract image area around the sampling point
    im = scimat.data(idxr, idxc, idxs);

    % compute low-pass filtered intensity of current voxel
    v(I) = sum(hbox(:) .* im(:)) / sum(hbox(:));

%     v(I) = sum(hbox(:) .* im(:)); % this is how Matlab's imfilter() does
%     it, which is incorrect near the edges because it reduces the value of
%     the pixels

end

% clear memory
clear h hbox im

% convert back to coordinates
x = scimat_index2world(x, scimat);

% compute coordinates of the two extreme voxels that define the image
% volume
cmin = scimat_index2world([1 1 1], scimat);
cmax = scimat_index2world(nin, scimat);

% if we have point coordinates with values like 100, 400, matrix L for the
% thin-plate spline weight computation is badly scaled. Thus, we make use
% of the invariability of thin-plate splines to scaling and make all the
% coordinate values <= 5.0 (if we make them <= 1.0, interpn gives an error
% saying that they grid is not monotonic)
K = max(cmax)/5;

% the warp will be defined as from the xyz-points to the corresponding
% intensity values

% compute what would be the size of the image if we downsample it
nmid = round(nin .* a);

% compute weights for thin-plate spline interpolation
w = pts_tps_weights(x/K, v);

% generate grid
[gx, gy, gz] = meshgrid(...
    linspace(cmin(1)/K, cmax(1)/K, nmid(2)), ...
    linspace(cmin(2)/K, cmax(2)/K, nmid(1)), ...
    linspace(cmin(3)/K, cmax(3)/K, nmid(3)));

% clear memory
scimat.data = [];

% interpolate intensity values for each point in the grid
scimat.data = pts_tps_map(x/K, v, [gx(:) gy(:) gz(:)], w, FASTINTERP, false);

% clear memory
clear gx gy gz

% reshape the interpolated values to go from a vector to an image volume
scimat.data = reshape(single(scimat.data), nmid);

% recover original size
scimat.data = tformarray(scimat.data, ...
    maketform('affine', [1/a(1) 0 0 0; 0 1/a(2) 0 0; 0 0 1/a(3) 0; 0 0 0 1]), ...
    makeresampler('linear', 'replicate'), ...
    1:length(scimat.axis), 1:length(scimat.axis), ...
    nin, [], []);


function loglik = compute_log_lik(P, S_bar, V, E_z, E_zz, RO, c, Tr, sigma_sq)

[K, T] = size(E_z);
J = size(S_bar, 2);

M_t = zeros(2*J, K);

loglik = - 0.5*T * (2*J*log(sigma_sq));
for t = 1:T,
   %%%%%%%%%%%%%%%%%%%%%%%%% Changed code here %%%%%%%%%%%%%%%%%%%%%%
   %R_t = RO{t};
   R_t  = c(t,1)*RO{t};
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

   Sdef = S_bar;
   for kk = 1:K,
      Sdef = Sdef + E_z(kk,t)*V((kk-1)*3+[1:3],:);

      M_t(1:J, kk) = (R_t(1,:)*V((kk-1)*3+[1:3],:))';
      M_t(J+1:end, kk) = (R_t(2,:)*V((kk-1)*3+[1:3], :))';
   end;

   invSigmaSq_p = eye(2*J)./sigma_sq;

   f_bar_t = R_t(1:2,:)*S_bar;
   f_bar_t = [f_bar_t(1,:) f_bar_t(2,:)]';

   f_t = [P(t, :) P(t+T, :)]';
   t_vect_t = [Tr(t,1)*ones(J,1); Tr(t,2)*ones(J,1)];

   covZ_t = E_zz((t-1)*K+1:t*K,:) - E_z(:,t)*E_z(:,t)';
   loglik = loglik - 0.5*(((f_t-f_bar_t-t_vect_t)./sigma_sq)'*(f_t-f_bar_t-t_vect_t)) + (((f_t-f_bar_t-t_vect_t)'./sigma_sq)*M_t*E_z(:,t)) ...
      - 0.5*trace(((M_t./sigma_sq)'*M_t) * E_zz((t-1)*K+1:t*K,:)) - 0.5*log(det(covZ_t));
end


function [condMu,condCov] = prtConditionalMvnMuCov(x,indices,globalMu,globalCov)
%[condMu,condCov] = conditionalMuCov(x,indices,globalMu,globalCov)

indices2 = indices;
indices1 = setdiff(1:length(globalMu),indices);

mu2 = globalMu(indices2);
mu1 = globalMu(indices1);

cov22 = globalCov(indices2,indices2);
cov12 = globalCov(indices1,indices2);
cov21 = globalCov(indices2,indices1);
cov11 = globalCov(indices1,indices1);

condMu = mu1 + (cov12*cov22^-1*(x - mu2)')';
condCov = cov11 - cov12*cov22^-1*cov21;


%+========================================================================+
%|                                                                        |
%|            This script uses the GYPSILAB toolbox for Matlab            |
%|                                                                        |
%| COPYRIGHT : Matthieu Aussal (c) 2017-2018.                             |
%| PROPERTY  : Centre de Mathematiques Appliquees, Ecole polytechnique,   |
%| route de Saclay, 91128 Palaiseau, France. All rights reserved.         |
%| LICENCE   : This program is free software, distributed in the hope that|
%| it will be useful, but WITHOUT ANY WARRANTY. Natively, you can use,    |
%| redistribute and/or modify it under the terms of the GNU General Public|
%| License, as published by the Free Software Foundation (version 3 or    |
%| later,  http://www.gnu.org/licenses). For private use, dual licencing  |
%| is available, please contact us to activate a "pay for remove" option. |
%| CONTACT   : matthieu.aussal@polytechnique.edu                          |
%|             francois.alouges@polytechnique.edu                         |
%| WEBSITE   : www.cmap.polytechnique.fr/~aussal/gypsilab                 |
%|                                                                        |
%| Please acknowledge the gypsilab toolbox in programs or publications in |
%| which you use it.                                                      |
%|________________________________________________________________________|
%|   '&`   |                                                              |
%|    #    |   FILE       : nrtDomEdge.m                                  |
%|    #    |   VERSION    : 0.50                                          |
%|   _#_   |   AUTHOR(S)  : Matthieu Aussal                               |
%|  ( # )  |   CREATION   : 25.11.2018                                    |
%|  / 0 \  |   LAST MODIF : 25.11.2018                                    |
%| ( === ) |   SYNOPSIS   : Triangular quadrature and integration         |
%|  `---'  |                                                              |
%+========================================================================+

% Cleaning
clear all
close all
clc

% Gypsilab path
run('../../addpathGypsilab.m')

% Parameters
Nvtx = 1e3;
rho  = 1;
tol  = 1e-3;

% Square mesh
mesh = mshSegment(Nvtx,rho);

% Domain
omega = dom(mesh,3);

% Finite element space
u = fem(mesh,'P1');
v = fem(mesh,'P1');

% Graphical representation
figure
plot(mesh)
hold on
plotNrm(mesh)
plot(omega)
plot(u,'or')
axis equal
alpha(0.1)
view(0,90)

% Numerical functions
Fx   = @(X) ones(size(X,1),1);
Fx3  = {Fx,Fx,Fx};
Fxy  = @(X,Y) (X(:,1)==Y(:,1));
Fxy3 = {Fxy,Fxy,Fxy};
Gxy  = @(X,Y) 1./(4*pi) * femGreenKernel(X,Y,'[exp(ikr)/r]',5);
Gxy3 = {Gxy,Gxy,Gxy};

%%%%%%%%%%%%%%% 2 ARGUMENTS %%%%%%%%%%%%%%%
disp('=========== 2 ARGUMENTS  ============')

% \int_{mesh(x)} f(x) dx
ref = integral(omega,Fx);
abs(ref-1)

% \int_{mesh(x)} f3(x) dx
sol = integral(omega,Fx3);
norm(sol{1}-ref,'inf')

%%%%%%%%%%%%%%% 3 ARGUMENTS %%%%%%%%%%%%%%%
disp('=========== 3 ARGUMENTS  ============')

% \int_{mesh(y)} f(x,y) dy
ref = integral(omega.qud,omega,Fxy);
abs(sum(ref,1)-1)

% \int_{mesh(y)} f3(x,y) dy
sol = integral(omega.qud,omega,Fxy3);
norm(sol{1}-ref,'inf')

%-------------------------------------

% \int_{mesh(x)} f(x,y) dx
ref = integral(omega,omega.qud,Fxy);
abs(sum(ref,2)-1)

% \int_{mesh(y)} f3(x,y) dx
sol = integral(omega,omega.qud,Fxy3);
norm(sol{1}-ref,'inf')

%-------------------------------------

% \int_{mesh(x)} f(x) psi(x) dx
ref = integral(omega,Fx,v);
abs(sum(ref,2) - 1)

% \int_{mesh(x)} f3(x) psi(x) dx
sol = integral(omega,Fx3,v);
norm(sol{1}-ref,'inf')

% \int_{mesh(x)} f(x) ntimes(psi(x)) dx
sol = integral(omega,Fx,ntimes(v));
norm(sol{2}-ref,'inf')

% \int_{mesh(x)} f3(x) ntimes(psi(x)) dx
sol = integral(omega,Fx3,ntimes(v));
norm(sol-ref,'inf')

%-------------------------------------

% \int_{mesh(x)} psi(x)' f(x)  dx
ref = integral(omega,u,Fx);
abs(sum(ref,1) - 1)

% \int_{mesh(x)} psi(x)' f3(x)  dx
sol = integral(omega,u,Fx3);
norm(sol{1}-ref,'inf')

% \int_{mesh(x)} ntimes(psi(x))' f(x)  dx
sol = integral(omega,ntimes(u),Fx);
norm(sol{2}-ref,'inf')

% \int_{mesh(x)} ntimes(psi(x))' f3(x)  dx
sol = integral(omega,ntimes(u),Fx3);
norm(sol-ref,'inf')

%-------------------------------------

% \int_{mesh(x)} psi(x)' psi(x) dx
ref = integral(omega,u,v);
abs(sum(sum(ref,1),2) - 1)

% \int_{mesh(x)} ntimes(psi(x))' psi(x) dx
sol = integral(omega,ntimes(u),v);
norm(sol{2}-ref,'inf')

% \int_{mesh(x)} psi(x)' ntimes(psi(x)) dx
sol = integral(omega,u,ntimes(v));
norm(sol{2}-ref,'inf')

% \int_{mesh(x)} ntimes(psi(x))' ntimes(psi(x)) dx
sol = integral(omega,ntimes(u),ntimes(v));
norm(sol-ref,'inf')

%%%%%%%%%%%%%%% 4 ARGUMENTS %%%%%%%%%%%%%%%
disp('=========== 4 ARGUMENTS  ============')

% \int_{mesh(x)} psi(x)' f(x) psi(x) dx
ref = integral(omega,u,Fx,v);
abs(sum(sum(ref,1),2) - 1)

% \int_{mesh(x)} ntimes(psi(x))' f(x) psi(x) dx
sol = integral(omega,ntimes(u),Fx,v);
norm(sol{2}-ref,'inf')

% \int_{mesh(x)} psi(x)' f3(x) psi(x) dx
sol = integral(omega,u,Fx3,v);
norm(sol{2}-ref,'inf')

% \int_{mesh(x)} psi(x)' f(x) ntimes(psi(x)) dx
sol = integral(omega,u,Fx,ntimes(v));
norm(sol{2}-ref,'inf')

% \int_{mesh(x)} ntimes(psi(x))' f3(x) psi(x) dx
sol = integral(omega,ntimes(u),Fx3,v);
norm(sol-ref,'inf')

% \int_{mesh(x)} ntimes(psi(x))' f(x) ntimes(psi(x)) dx
sol = integral(omega,ntimes(u),Fx,ntimes(v));
norm(sol-ref,'inf')

% \int_{mesh(x)} (psi(x))' f3(x) ntimes(psi(x)) dx
sol = integral(omega,u,Fx3,ntimes(v));
norm(sol-ref,'inf')

% \int_{mesh(x)} ntimes(psi(x))' (f3(x)) ntimes(psi(x)) dx
sol = integral(omega,ntimes(u),Fx3,ntimes(v));
norm(sol,'inf')

%-------------------------------------

% \int_{mesh(y)} f(x,y) psi(y) dy
ref = integral(omega.qud,omega,Fxy,v);
abs(sum(sum(ref,1),2) - 1)

% \int_{mesh(y)} f(x,y) ntimes(psi(y)) dy
sol = integral(omega.qud,omega,Fxy,ntimes(v));
norm(sol{2}-ref,'inf')

% \int_{mesh(y)} f(3x,y) psi(y) dy
sol = integral(omega.qud,omega,Fxy3,v);
norm(sol{2}-ref,'inf')

% \int_{mesh(y)} f3(x,y) ntimes(psi(y)) dy
sol = integral(omega.qud,omega,Fxy3,ntimes(v));
norm(sol-ref,'inf')

%-------------------------------------

% \int_{mesh(x)} psi(x)' f(x,y) dx
ref = integral(omega,omega.qud,u,Fxy);
abs(sum(sum(ref,1),2) - 1)

% \int_{mesh(x)} psi(x)' f3(x,y) dx
sol = integral(omega,omega.qud,u,Fxy3);
norm(sol{2}-ref,'inf')

% \int_{mesh(x)} ntimes(psi(x))' f(x,y) dx
sol = integral(omega,omega.qud,ntimes(u),Fxy);
norm(sol{2}-ref,'inf')

% \int_{mesh(x)} ntimes(psi(x))' f3(x,y) dx
sol = integral(omega,omega.qud,ntimes(u),Fxy3);
norm(sol-ref,'inf')

%%%%%%%%%%%%%% 5 ARGUMENTS %%%%%%%%%%%%%%%
disp('=========== 5 ARGUMENTS  ============')

% \int_{mesh(y)} G(x,y) psi(y) dy
ref = integral(mesh.ctr,omega,Gxy,v);
sol = integral(mesh.ctr,omega,Gxy,v,tol);
norm(full(sol)-ref,'inf')./norm(ref,'inf')

% % \int_{mesh(y)} G3(x,y) psi(y) dy
sol = integral(mesh.ctr,omega,Gxy3,v,tol);
norm(full(sol{1})-ref,'inf')./norm(ref,'inf')

% \int_{mesh(y)} G(x,y) ntimes(psi(y)) dy
sol = integral(mesh.ctr,omega,Gxy,ntimes(v),tol);
norm(full(sol{2})-ref,'inf')./norm(ref,'inf')

% \int_{mesh(y)} G3(x,y) ntimes(psi(y)) dy
sol = integral(mesh.ctr,omega,Gxy3,ntimes(v),tol);
norm(full(sol)-ref,'inf')./norm(ref,'inf')

%-------------------------------------

% \int_{mesh(x)} psi(x)' G(x,y) dx
ref = integral(omega,mesh.ctr,u,Gxy);
sol = integral(omega,mesh.ctr,u,Gxy,tol);
norm(full(sol)-ref,'inf')./norm(ref,'inf')

% \int_{mesh(x)} psi(x)' G3(x,y) dx
sol = integral(omega,mesh.ctr,u,Gxy3,tol);
norm(full(sol{1})-ref,'inf')./norm(ref,'inf')

% \int_{mesh(x)} ntimes(psi(x))' G(x,y) dx
sol = integral(omega,mesh.ctr,ntimes(u),Gxy,tol);
norm(full(sol{2})-ref,'inf')./norm(ref,'inf')

% \int_{mesh(x)} ntimes(psi(x))' G3(x,y) dx
sol = integral(omega,mesh.ctr,ntimes(u),Gxy3,tol);
norm(full(sol)-ref,'inf')./norm(ref,'inf')

%%%%%%%%%%%%%%% 6 ARGUMENTS %%%%%%%%%%%%%%%
disp('=========== 6 ARGUMENTS  ============')

% \int_{mesh(x)} \int_{mesh(y)} psi(x)' G(x,y) psi(y) dx dy
ref = integral(omega,omega,u,Gxy,v);
sol = integral(omega,omega,u,Gxy,v,tol);
norm(full(sol)-ref,'inf')./norm(ref,'inf')

% \int_{mesh(x)} \int_{mesh(y)} ntimes(psi(x))' G(x,y) psi(y) dx dy
ref = integral(omega,omega,ntimes(u),Gxy,v);
sol = integral(omega,omega,ntimes(u),Gxy,v,tol);
norm(full(sol{2})-ref{2},'inf')./norm(ref{2},'inf')

% \int_{mesh(x)} \int_{mesh(y)} psi(x)' G3(x,y) psi(y) dx dy
ref = integral(omega,omega,u,Gxy3,v);
sol = integral(omega,omega,u,Gxy3,v,tol);
norm(full(sol{2})-ref{2},'inf')./norm(ref{2},'inf')

% \int_{mesh(x)} \int_{mesh(y)} psi(x)' G(x,y) ntimes(psi(y)) dx dy
ref = integral(omega,omega,u,Gxy,ntimes(v));
sol = integral(omega,omega,u,Gxy,ntimes(v),tol);
norm(full(sol{2})-ref{2},'inf')./norm(ref{2},'inf')

% \int_{mesh(x)} \int_{mesh(y)} ntimes(psi(x))' G3(x,y) psi(y) dx dy
ref = integral(omega,omega,ntimes(u),Gxy3,v);
sol = integral(omega,omega,ntimes(u),Gxy3,v,tol);
norm(full(sol)-ref,'inf')./norm(ref,'inf')

% \int_{mesh(x)} \int_{mesh(y)} psi(x)' G3(x,y) ntimes(psi(y)) dx dy
ref = integral(omega,omega,u,Gxy3,ntimes(v));
sol = integral(omega,omega,u,Gxy3,ntimes(v),tol);
norm(full(sol)-ref,'inf')./norm(ref,'inf')

% \int_{mesh(x)} \int_{mesh(y)} ntimes(psi(x))' G(x,y) ntimes(psi(y)) dx dy
ref = integral(omega,omega,ntimes(u),Gxy,ntimes(v));
sol = integral(omega,omega,ntimes(u),Gxy,ntimes(v),tol);
norm(full(sol)-ref,'inf')./norm(ref,'inf')

% \int_{mesh(x)} \int_{mesh(y)} ntimes(psi(x))' G3(x,y) ntimes(psi(y)) dx dy
ref = integral(omega,omega,ntimes(u),Gxy3,ntimes(v));
sol = integral(omega,omega,ntimes(u),Gxy3,ntimes(v),tol);
norm(full(sol)-ref,'inf')

disp('~~> Michto gypsilab !')


function [apexPoints,signVals,exitCode]=trace2EarthMagApex(zCart,modSelParam,a,f,RelTol,AbsTol,maxSteps)
%TRACE2EARTHMAGAPEX Given a point in the in the International Terrestial
%           Reference System (ITRS), a type of Earth-Centered Earth-Fixed
%           (ECEF) system trace along the magnetic field line of the Earth
%           until the apex is found. The apex is the highest point of the
%           magnetic field line above the reference ellipsoid. Such tracing
%           is used for determining apex coordinates and quasi-dipole
%           coordinates, both of which play a role in ionospheric analyses.
%
%INPUTS: zCart One or more points given in Cartesian coordinates in the
%              ITRS with units of meters. zCart is a 3XN matrix with each
%              column having the format [x;y;z]. One would generally
%              assume that points are not underground.
%  modSelParam An optional parameter selecting or providing the magnetic
%              field model to use. If omitted or an empty matrix is
%              passed, the International Geomagnetic Reference Field
%              (IGRF) at the latest epoch of the model is used via the
%              function getIGRFCoeffs. Possible other values are
%              1) year The parameters of the IGRF for the specified epoch
%                 year are used. The year is in the Gregorian calendar and
%                 is specified as noted in the comments to the
%                 getIGRFCoeffs  function.
%              2) modSelParam is a structure where modSelParam.algorithm
%                 selects the algorithm to use Possible values for
%                 modSelParam.algorithm are 'IGRF', 'WMM' and 'preloaded'.
%                 If modSelParam.algorithm is 'IGRF or 'WMM', then the IGRF
%                 or the World Magnetic Model (WMM) is used at the
%                 fractional year specified by modSelParam.year (using the
%                 function getIGRFCoeffs or getWMMCoeffs). If
%                 modSelParam.algorithm is preloaded, then the parameters
%                 for the model are given by the modSelParam.C,
%                 modSelParam.S, modSelParam.a, and modSelParam.c, where
%                 the parameters have the same format as the fully
%                 normalized outputs of getIGRFCoeffs or getWMMCoeffs. See
%                 comments below for the use of custom coeffients.
%            a The semi-major axis of the reference ellipsoid. The apex is
%              defined in terms of a maximum magnetic field line height
%              above the reference ellipsoid. If this argument is omitted
%              or an empty matrix is passed, the value in
%              Constants.WGS84SemiMajorAxis is used.
%            f The flattening factor of the reference ellipsoid. If this
%              argument is omitted or an empty matrix is passed, the value
%              in Constants.WGS84Flattening is used.
%       RelTol The maximum relative error tolerance allowed for the
%              Runge-Kutta algorithm to trace the path towards the apex.
%              If omitted or an empty matrix is passed, the default value
%              of 1e-6 is used.
%       AbsTol The absolute error tolerance allowed, a positive scalar.
%              If omitted or an empty matrix is passed, the default value
%              of 1e-9 is used. This is used in the Runge-Kutta algorithm
%              to trace along the path towards the apex, and it is used
%              for the fminbnd function to find the apex location once it
%              has been bounded.
%     maxSteps The maximum allowable number of steps to perform the
%              adaptive Runge-Kutta integration along a magnetic field
%              line to find the apex. If omitted or an empty matrix is
%              passed, the default of 1024 is used.
%
%OUTPUS: apexPoints A 3XN matrix of the apex points corresponding to the
%               points in zCart. The apex points are obtained by tracing the
%               magnetic field line starting from zCart until reaching the
%               point farthest from the reference ellipsoid. Near the
%               geomagnetic poles, these points will be very far from the
%               Earth. Thus, if the point ends up being 1e37 m or more away
%               from the Earth, then integration is stopped and the
%               components of apexPoints are just set to Inf (rather than
%               returning an error). If  exitCode~=0, for any point, an
%               empty matrix is returned.
%      signVals An NX1 vector of values (+1, or -1) indicating whether the
%               direction to the apex point along the field line was
%               obtained by tracing in the direction of the magnetic field
%               or opposite to it. This can be used to determine which
%               magnetic hemisphere the points in zCart are in, because
%               +1 means that one starts closer to the magnetic North pole
%               (geographic South pole), and -1 means that one starts
%               closer to the magnetic South pole (geographic North pole).
%               If an error occurs, an empty matrix is returned.
%      exitCode A NX1 vector of codes indicating whether an error occurred.
%               If an error occurs, then the algorithm is halted at the
%               point causing the error.
%               0: Integration was successful.
%               1: Unable to get a small enough step size.
%               2: Apex not found within the maximum number of iterations.
%               3: Non-finite number encountered.
%               4: Problems performing a line search to the peak point.
%
%Basically, starting at the given point, one traces along a magnetic field
%line until reaching the highest point above the reference ellipsoid as
%defined in [2]. The tracing is done using an order 5(4) adaptive
%Runge-Kutta method until the direction of the field line is descending, in
%which case the peak is bounded between the last two points on the field
%line. An interpolation routine is then used with fminbnd to find the peak.
%This assumes that there is only one peak, which is safe to assume with the
%IMM and WMM. On the other hand, if using a custom magnetic field model,
%there might be more peaks and this function will not return the correct
%solution.
%
%Note that in [1], the definition of the apex is given in terms of a
%maximum height or a magnetic field line above the geoid, not above the
%reference ellipsoid (which is an approximation of the geoid). The
%definition with respect to the geoid seems to not be used in the
%literature. Moreover, the fast undulations of the geoid would make a
%magnetic field line have more than one peak, requiring that one find every
%peak to determine where the apex is.
%
%REFERENCES:
%[1] T. E. VanZandt, W. L. Clark, and J. M. Warnock, "Magnetic apex
%    coordinates: A magnetic coordinate system for the ionospheric f2
%    layer," Journal of Geophysical Research, vol. 77, no. 13, pp. 2406-
%    2411, 1 May 1972.
%[2] A. D. Richmond, "Ionospheric electrodynamics using magnetic apex
%    coordinates," Journal of Geomagnetism and Geoelectricity, vol. 47,
%    no. 2, pp. 191-212, 1995.
%
%June 2015 David F. Crouse, Naval Research Laboratory, Washington D.C.
%(UNCLASSIFIED) DISTRIBUTION STATEMENT A. Approved for public release.

if(nargin<2||isempty(modSelParam))
    %If no magnetic model is specified, use the IGRF at the reference
    %epoch.
    [C,S,aMagMod,cMagMod]=getIGRFCoeffs([],true);
elseif(isa(modSelParam,'double'))
    %If just a scalar is passed, assume that it is the epoch year for the
    %IGRF.
    [C,S,aMagMod,cMagMod]=getIGRFCoeffs(modSelParam,true);
else
    %Otherwise, a structrue should be passed indicating the model and the
    %year.
    switch(modSelParam.algorithm)
        case 'WMM'%World Magnetic Model
            [C,S,aMagMod,cMagMod]=getWMMCoeffs(modSelParam.year,true);
        case 'IGRF'%Intergnational Geomagnetic Reference Field
            [C,S,aMagMod,cMagMod]=getIGRFCoeffs(modSelParam.year,true);
        case 'preloaded'%Custom coefficients.
            C=modSelParam.C;
            S=modSelParam.S;
            aMagMod=modSelParam.a;
            cMagMod=modSelParam.c;
        otherwise
            error('Unknown model specified')
    end
end

if(nargin<3||isempty(a))
    a=Constants.WGS84SemiMajorAxis;
end

if(nargin<4||isempty(f))
    f=Constants.WGS84Flattening;
end

if(nargin<5||isempty(RelTol))
    RelTol=1e-6;
end

if(nargin<6||isempty(AbsTol))
    AbsTol=1e-9;
end

if(nargin<7||isempty(maxSteps))
    maxSteps=1024;
end

%The parameters selecting the Runge-Kutta algorithm to use for the
%numerical integration.
order=5;
solutionChoice=0;

numPoints=size(zCart,2);
apexPoints=zeros(3,numPoints);
signVals=zeros(numPoints,1);
exitCode=zeros(numPoints,1);
for curPoint=1:numPoints
    zITRS=zCart(:,curPoint);

    %The initial stepsize is arbitrarily set to 10km.
    deltaS=10e3;
    %The maximum stepsize is arbitrarily set very high, so that it can be
    %quickly detected when the apex is extremely far away, because the
    %chosen point is very near the magnetic pole.
    deltaSMaxMag=1e36;

    xCur=zITRS;%The initial Cartesian location.
    sCur=0;%Distance traveled from the initial point is zero.

    %First, we have to decide which direction to go. We want to go in the
    %direction of increasing ellipsoidal height.
    dxdsCur=dxds(xCur,1,C,S,aMagMod,cMagMod);%The positive direction.

    uVert=getEllipsVert(zITRS,a,f);

    %If the angle between the direction and the local vertical is >90
    %degrees, then it is a decreasing direction. If it is greater, then it
    %is an increasing direction. This is the same as checking whether the
    %dot product is negative (the dot product is proportional to the cosine
    %of the angle).
    if(dot(uVert,dxdsCur)<0)
        signVal=-1;%Go the other way.
    else
        signVal=1;
    end
    signVals(curPoint)=signVal;

    %The path direction function.
    derivFun=@(x,s)dxds(x,signVal,C,S,aMagMod,cMagMod);

    %Now, we integrate along the path until the next point takes us to a
    %location where the direction along the magnetic field line is no longer
    %going in a direction of increasing ellipsoidal height. That means that we
    %have passed the apex. Thus, the apex height can be interpolated beteen
    %those final two points.
    apexPoint=[];
    for curStep=1:maxSteps
        xPrev=xCur;
        sPrev=sCur;

        %Arbitrary minimum step size.
        deltaSMinMag=2^4*eps(sCur);

        [deltaS,xCur,sCur,k,dxdsCur,exitCode(curPoint)]=performOneAdaptiveRKStep(xCur,sCur,derivFun,deltaS,deltaSMinMag,deltaSMaxMag,dxdsCur,order,solutionChoice,AbsTol,RelTol);

        if(exitCode(curPoint)~=0)
            signVals=[];
            apexPoints=[];
            return;
        end

        %Determine whether the new point is still a point of increasing
        %ellipsoidal height.
        uVert=getEllipsVert(xCur,a,f);

        %If the peak has been reached.
        if(dot(uVert,dxdsCur)<0)
            %Interpolate to find the peak.

            %Get a Hermite interpolating polynomial over the step. Note that the
            %interpolating polynomial takes a parameter from 0->1 indicating the
            %fraction of the distance traveled between sPrev and sCur.
            [interpPolyA,interpPolyC]=RKInterpPolys(xPrev,sPrev,xCur,sCur,derivFun,order,solutionChoice,k);

            %We will now do a simple line search to find where the direction of
            %the magnetic field vector is 90 degrees offset.
            costFun=@(sFracEst)levelCostFun(sFracEst,interpPolyA,interpPolyC,a,f);
            try
                options=optimset('TolX',AbsTol);
                minFrac=fminbnd(costFun,0,1,options);
            catch%If some error in fminbnd occurred.
                apexPoints=[];
                exitCode(curPoint)=4;
                return
            end

            apexPoint=polyValNewton(minFrac,interpPolyA,interpPolyC);

            break;
        elseif(Cart2Ellipse(xCur,[],a,f)>=1e37)
            %If the ellipsoidal height exceeds 1e37m, then just declare it
            %to be at infinity. This helps near the magnetic poles.
            apexPoint=[Inf;Inf;Inf];
        end
    end

    if(isempty(apexPoint))
       %If the maximum number of iterations was achieved without hitting the
       %apex point, then return with an error.
        apexPoints=[];
        signVals=[];
        exitCode(curPoint)=2;
        return;
    end

    %We have the apexPoint. We can now compute the apex coordinates.
    apexPoints(:,curPoint)=apexPoint;
    exitCode(curPoint)=0;
end

end

function uVert=getEllipsVert(xCart,a,f)
%Get a vector pointing in the direction of the ellipsoidal vertical. This
%requires the position to be converted into ellipsoidal coordinates.

    ellipsCur=Cart2Ellipse(xCart,[],a,f);
    justVertical=true;
    uVert=getENUAxes(ellipsCur,justVertical,a,f);
end

function costVal=levelCostFun(sFracEst,interpPolyA,interpPolyC,a,f)
%The cost function to determine when the angle between the local vertical
%and the magnetic field is 90 degrees. We are trying to drive the dot
%product to zero.

    xValCart=polyValNewton(sFracEst,interpPolyA,interpPolyC);
    dxdsFracCart=polyDerValNewton(sFracEst,interpPolyA,interpPolyC);

    uVert=getEllipsVert(xValCart,a,f);

    costVal=abs(dot(uVert,dxdsFracCart));
end

function derivVal=dxds(xCart,signVal,C,S,aMagMod,cMagMod)
%The function providing the derivative of position with respect to
%arclength for tracing out the field line.

    pointSpher=Cart2Sphere(xCart);
    [~,gradV]=spherHarmonicEval(C,S,pointSpher,aMagMod,cMagMod);
    B=-gradV;%The magnetic flux

    %Integration is performed in space, so the normalized B vector is the
    %position derivative for the differential equation. The problem of running
    %into a point where B=0 should not arise.
    derivVal=signVal*B/norm(B);

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 [ x, y, z, w ] = ld0302 ( )

%*****************************************************************************80
%
%% LD0302 computes the 302 point Lebedev angular grid.
%
%  Modified:
%
%    14 September 2010
%
%  Author:
%
%    Dmitri Laikov
%
%  Reference:
%
%    Vyacheslav Lebedev, Dmitri Laikov,
%    A quadrature formula for the sphere of the 131st
%    algebraic order of accuracy,
%    Russian Academy of Sciences Doklady Mathematics,
%    Volume 59, Number 3, 1999, pages 477-481.
%
%  Parameters:
%
%    Output, real X(N), Y(N), Z(N), W(N), the coordinates
%    and weights of the points.
%
  n = 0;
  x = zeros(302,1);
  y = zeros(302,1);
  z = zeros(302,1);
  w = zeros(302,1);
  a = 0.0;
  b = 0.0;
  v = 0.8545911725128148E-03;
  [ n, x, y, z, w ] = gen_oh ( 1, n, a, b, v, x, y, z, w );
  v = 0.3599119285025571E-02;
  [ n, x, y, z, w ] = gen_oh ( 3, n, a, b, v, x, y, z, w );
  a = 0.3515640345570105;
  v = 0.3449788424305883E-02;
  [ n, x, y, z, w ] = gen_oh ( 4, n, a, b, v, x, y, z, w );
  a = 0.6566329410219612;
  v = 0.3604822601419882E-02;
  [ n, x, y, z, w ] = gen_oh ( 4, n, a, b, v, x, y, z, w );
  a = 0.4729054132581005;
  v = 0.3576729661743367E-02;
  [ n, x, y, z, w ] = gen_oh ( 4, n, a, b, v, x, y, z, w );
  a = 0.9618308522614784E-01;
  v = 0.2352101413689164E-02;
  [ n, x, y, z, w ] = gen_oh ( 4, n, a, b, v, x, y, z, w );
  a = 0.2219645236294178;
  v = 0.3108953122413675E-02;
  [ n, x, y, z, w ] = gen_oh ( 4, n, a, b, v, x, y, z, w );
  a = 0.7011766416089545;
  v = 0.3650045807677255E-02;
  [ n, x, y, z, w ] = gen_oh ( 4, n, a, b, v, x, y, z, w );
  a = 0.2644152887060663;
  v = 0.2982344963171804E-02;
  [ n, x, y, z, w ] = gen_oh ( 5, n, a, b, v, x, y, z, w );
  a = 0.5718955891878961;
  v = 0.3600820932216460E-02;
  [ n, x, y, z, w ] = gen_oh ( 5, n, a, b, v, x, y, z, w );
  a = 0.2510034751770465;
  b = 0.8000727494073952;
  v = 0.3571540554273387E-02;
  [ n, x, y, z, w ] = gen_oh ( 6, n, a, b, v, x, y, z, w );
  a = 0.1233548532583327;
  b = 0.4127724083168531;
  v = 0.3392312205006170E-02;
  [ n, x, y, z, w ] = gen_oh ( 6, n, a, b, v, x, y, z, w );

  return
end


function fun = calcS1Density(x,varargin)

if ~check_option(varargin,'sigma')
  % automatic bandwidth selection
  k = ((280*pi^0.5)/729)^0.2/2/pi;
  s = min(std(x), iqr(x)/1.349);
  sigma = k*s*length(x)^-0.2;
else
  sigma = get_option(varargin,'sigma');
end

% compute bandwidth dependent from sigma
N = round(4/sigma);

y = get_option(varargin,'weights',ones(size(x)));
fun = S1FunHarmonic.quadrature(x,y,'bandwidth',N,varargin{:});

% convolution
fun.fhat(1) = 0;
fun.fhat(2:end) = fun.fhat(2:end) .* exp(- 0.5*sigma.^2 * (-N:N).^2).';

% normalize
fun.fhat = fun.fhat ./ fun.fhat(N+2);

end

function test

x = gB.direction.rho;
norm(gB.direction)

out = linspace(-pi,pi,1000);

plot(out,abs(plan2.f))

%plan.fhat
figure(2)
histogram(omega)

% make Gaussian kernel
ti = 1:length(Tp);
mu = length(ti)/2 + 1;
G = (exp( -((ti - mu).^2)./ (2*sigma^2) ))';
% convolve kernel with data
pdf = ifft(fft(Tp).*fft(G));
% remove padding
pdf = pdf(tn-t2+1:tn-t2+tn);
% normalize values
pdf = pdf./sum(pdf);

end


function gt = Kernel_TVHP( times, landmarks, sigma, type )

dt = repmat(landmarks(:), [1, length(times(:))])...
    - repmat(times(:)', [length(landmarks(:)), 1]);

switch type
    case 'gauss'
        gt = exp(-(dt.^2)./(2*sigma^2));

    case 'exp'
        gt = exp(sigma.*dt);
        gt(gt>1) = 0;

end


% STATCOND  - compare two or more data conditions statistically using
%               standard parametric or nonparametric permutation-based ANOVA
%               (1-way or 2-way) or t-test methods. Parametric testing uses
%               FCDF from the Matlab Statistical Toolbox.
% Usage:
%          >> [stats, df, pvals, surrog] = statcond( data, 'key','val'... );
%
% Inputs:
%   data       = one-or two-dimensional cell array of data matrices.
%                   For nonparametric, permutation-based testing, the
%                last dimension of the data arrays (which may be of up to
%                4 dimensions) is permuted across conditions, either in
%                a 'paired' fashion (not changing the, e.g., subject or
%                trial order in the last dimension) or in an umpaired
%                fashion (not respecting this order). If the number of
%                elements in the last dimension is not the same across
%                conditions, the 'paired' option is turned 'off'.  Note:
%                All other dimensions MUST be constant across conditions.
%                   For example, consider a (1,3) cell array of matrices
%                of size (100,20,x) each holding a (100,20) time/frequency
%                transform from each of x subjects. Only the last dimension
%                (here x, the number of subjects) may differ across the
%                three conditions.
%                   The test used depends on the size of the data array input.
%                When the data cell array has 2 columns and the data are
%                paired, a paired t-test is performed; when the data are
%                unpaired, an unpaired t-test is performed. If 'data'
%                has only one row (paired or unpaired) and more than 2
%                columns, a one-way ANOVA is performed. If the data cell
%                array contains several rows and columns, and the data is
%                paired, a two-way repeated measure ANOVA is performed.
%                NOTE THAT IF THE DATA is unpaired, EEGLAB will use a
%                balanced 1 or 2 way ANOVA and parametric results might not
%                be meaningful (bootstrap and permstatcondutation should be fine).
%
% Optional inputs:
%   'paired'   = ['on'|'off'] pair the data array {default: 'on' unless
%                the last dimension of data array is of different lengths}.
%   'method'   = ['perm'|'bootstrap'|'param'] method for computing the p-values:
%                 'param' or 'parametric' = parametric testing (standard ANOVA
%                                           or t-test);
%                 'perm' or 'permutation' = non-parametric testing using
%                                           surrogate data
%                 'bootstrap' = non-parametric bootstrap
%                  made by permuting the input data {default: 'param'}
%   'naccu'    = [integer] Number of surrogate data copies to use in 'perm'
%                 or 'bootstrap' method estimation (see above) {default: 200}.
%   'verbose'  = ['on'|'off'] print info on the command line {default: 'on'}.
%   'variance' = ['homegenous'|'inhomogenous'] this option is exclusively
%                for parametric statistics using unpaired t-test. It allows
%                to compute a more accurate value for the degree of freedom
%                using the formula for inhomogenous variance (see
%                ttest2_cell function). Default is 'inhomegenous'.
%   'surrog'   = surrogate data array (see output).
%   'stats'    = F- or T-value array (see output).
%   'tail'     = ['one'|'two'] run one-tailed (F-test) or two tailed
%                (T-test). This option is only relevant when using the
%                'surrog' input. Otherwise it is ignored.
%   'forceanova' = ['on'|'off'] force the use of ANOVA calculation even
%                for 2x1 designs. Default is 'off'.
%   'alpha'    = [float] p-value threshold value. Allow returning
%                confidence intervals and mask (requires structoutput below).
%   'structoutput' = ['on'|'off'] return an output structure instead of
%                the regular output. Allow to output mask and confidence
%                intervals.
%   'cluster'  = ['on'|'off'] cluster correction for multiple comparison.
%                Only functional when alpha is set (if alpha is NaN, it
%                sets it to 0.05) and for 1-way Anova or t-test.
%
% Legacy parameters:
%   'threshold' - now 'alpha'
%   'mode'      - now 'method'
%
% Outputs:
%   stats      = F- or T-value array of the same size as input data without
%                the last dimension. A T value is returned only when the data
%                includes exactly two conditions.
%   df         = degrees of freedom, a (2,1) vector, when F-values are returned
%   pvals      = array of p-values. Same size as input data without the last
%                data dimension. All returned p-values are two-tailed.
%   surrog     = surrogate statistic values (same size as stats output with the last
%                dimension filled with a number ('naccu') of surrogate data sets.
%
% Important note: When a two-way ANOVA is performed, outputs are cell arrays
%                 with three elements: output(1) = row effects;
%                 output(2) = column effects; output(3) = interactions
%                 between rows and columns.
%
% Examples:
%      >> a = { rand(1,10) rand(1,10)+0.5 }; % pseudo 'paired' data vectors
%         [t df pvals] = statcond(a);        % perform paired t-test
%           pvals =
%              5.2807e-04 % standard t-test probability value
%         % Note: for different RAND outputs, results will differ.
%
%         [t df pvals surog] = statcond(a, 'method', 'perm', 'naccu', 2000);
%           pvals =
%              0.0065 % nonparametric t-test using 2000 permuted data sets
%
%         a = { rand(2,11) rand(2,10) rand(2,12)+0.5 }; % pseudo 'unpaired'
%         [F df pvals] = statcond(a); % perform an unpaired ANOVA
%           pvals =
%              0.00025 % p-values for difference between columns
%              0.00002 % for each data row
%
%         a = { rand(3,4,10) rand(3,4,10) rand(3,4,10); ...
%               rand(3,4,10) rand(3,4,10) rand(3,4,10)+0.5 };
%         % pseudo (2,3)-condition data array, each entry containing
%         %                                    ten (3,4) data matrices
%         [F df pvals] = statcond(a);  % perform a paired 2-way ANOVA
%         % Output:
%           pvals{1} % a (3,4) matrix of p-values; effects across rows
%           pvals{2} % a (3,4) matrix of p-values; effects across columns
%           pvals{3} % a (3,4) matrix of p-values; interaction effects
%                                      % across rows and columns
%
% Author: Arnaud Delorme, SCCN/INC/UCSD, La Jolla, 2005-
%         With thanks to Robert Oostenveld for fruitful discussions
%         and advices on this function.
%
% See also: ANOVA1_CELL, ANOVA2_CELL, ANOVA2RM_CELL, FCDF

% perform a paired t-test
% -----------------------
% a = { rand(2,10) rand(2,10) };
% [t df pval] = statcond(a); pval
% [h p t stat] = ttest( a{1}(1,:), a{2}(1,:)); p
% [h p t stat] = ttest( a{1}(2,:), a{2}(2,:)); p
%
% compare significance levels
% --------------------------
% a = { rand(1,10) rand(1,10) };
% [F df pval] = statcond(a, 'method', 'perm', 'naccu', 200); pval
% [h p t stat] = ttest( a{1}(1,:), a{2}(1,:)); p

% Copyright (C) Arnaud Delorme
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function [ ori_vals, df, pvals, surrogval ] = statcond( data, varargin );

    if nargin < 1
        help statcond;
        return;
    end
    try, warning('off', 'MATLAB:divideByZero'); catch, end;

    if exist('finputcheck')
        g = finputcheck( varargin, { 'naccu'      'integer'   [1 Inf]             200;
                                     'method'     'string'    { 'param','parametric','perm','permutation','bootstrap' }  'param';
                                     'mode'       'string'    { }                 '';
                                     'paired'     'string'    { 'on','off','auto' }      'auto';
                                     'surrog'     { 'real','cell' }      []       [];
                                     'stats'      { 'real','cell' }      []       [];
                                     'structoutput' 'string'  { 'on','off' }      'off';
                                     'forceanova'   'string'  { 'on','off' }      'off';
                                     'arraycomp'  'string'    { 'on','off' }      'on';
                                     'cluster'    'string'    { 'on','off' }      'off';
                                     'alpha'      'real'      []                  NaN;
                                     'tail'       'string'    { 'one','both','upper','lower'}    'both';
                                     'variance'   'string'    { 'homogenous','inhomogenous' }    'inhomogenous';
                                     'returnresamplingarray' 'string'    { 'on','off' }      'off';
                                     'verbose'    'string'    { 'on','off' }      'on' }, 'statcond');
        if ischar(g), error(g); end
    else
        g = struct(varargin{:});
        if ~isfield(g, 'naccu'),     g.naccu = 200; end
        if ~isfield(g, 'method'),    g.method  = 'param'; end
        if ~isfield(g, 'paired'),    g.paired = 'auto'; end
        if ~isfield(g, 'surrog'),    g.surrog = []; end
        if ~isfield(g, 'orivals'),   g.orivals = []; end
        if ~isfield(g, 'arraycomp'), g.arraycomp = 'on'; end
        if ~isfield(g, 'verbose'),   g.verbose = 'on'; end
        if ~isfield(g, 'tail'),      g.tail = 'both'; end
        if ~isfield(g, 'variance'),  g.variance = 'homogenous'; end
        if ~isfield(g, 'structoutput'), g.structoutput = 'on'; end
        if ~isfield(g, 'returnresamplingarray'),   g.returnresamplingarray = 'off'; end
    end
    if ~isempty(g.mode), g.method = g.mode; end
    if size(data,2) == 1, data  = transpose(data); end % cell array transpose

    % other settings
    % --------------
    if strcmpi(g.method, 'parametric'), g.method = 'param'; end
    if strcmpi(g.method, 'permutation'), g.method = 'perm'; end
    if strcmpi(g.verbose, 'on'), verb = 1; else verb = 0; end
    if strcmp(g.method, 'param' ) && exist('fcdf') ~= 2
      myfprintf('on',['statcond(): parametric testing requires fcdf() \n' ...
               '            from the Matlab StatsticaL Toolbox.\n' ...
               '            Running nonparametric permutation tests\n.']);
      g.method = 'perm';
    end
    g.naccu = round(g.naccu);

    % pairing
    % -------
    paired{1} = 'on';
    if size(data,1) == 1 && length(unique(cellfun('size', data, ndims(data{1}) ))) > 1
        paired{1} = 'off';
    else
        paired{1} = 'on';
        for iCol = 1:size(data,2)
            if length(unique(cellfun('size', data(:,iCol), ndims(data{1}) ))) > 1
                paired{1} = 'off';
            end
        end
        paired{2} = 'on';
        for iRow = 1:size(data,1)
            if length(unique(cellfun('size', data(iRow,:), ndims(data{1}) ))) > 1
                paired{2} = 'off';
            end
        end
    end
    if length(paired) > 1
        if (strcmpi(paired{1}, 'off') && strcmpi(paired{2}, 'on')) || ...
                (strcmpi(paired{1}, 'on') && strcmpi(paired{2}, 'off'))
            myfprintf(g.verbose, 'Possible mixed paired and unpaired independent variables, using balanced Anova (which assumes all unpaired)\n');
            paired{1} = 'off';
        end
    end
    if strcmpi(g.paired, 'auto')
        g.paired = paired{1};
    else
        if strcmpi(g.paired, 'on') && strcmpi(paired{1}, 'off')
            myfprintf(g.verbose, 'You set to use paired statistics but the number of cases differs\n');
            g.paired = 'off';
        end
    end

    % reshape matrices
    % ----------------
    nd = size(data{1});
    nd = nd(1:end-1);
    for index = 1:prod(size(data))
        data{index} = reshape(data{index}, [prod(nd) size(data{index},myndims(data{index}))]);
    end

    if ~strcmpi(g.method, 'param') && isempty(g.surrog)
         tmpsize   = size(data{1});
         surrogval = zeros([ tmpsize(1:end-1) g.naccu ], 'single');
    else surrogval = [];
    end

    % check for NaNs or Inf
    % ---------------------
    for iDat = 1:length(data(:))
        if any(isnan(reshape(data{iDat}, prod(size(data{iDat})),1))) || ...
                any(isinf(reshape(data{iDat}, prod(size(data{iDat})),1)))
            error('Statcond: One of the input array contains NaNs or Infinite values');
        end
    end

    % bootstrap flag
    % --------------
    if strcmpi(g.method, 'bootstrap'), bootflag = 1;
    else                               bootflag = 0;
    end

    if isempty(g.surrog)

        % return resampling array
        % -----------------------
        if strcmpi(g.returnresamplingarray, 'on')
            if strcmpi(g.arraycomp, 'on')
                ori_vals = surrogdistrib( data, 'method', g.method, 'pairing', g.paired, 'naccu', g.naccu);
            else
                ori_vals = surrogdistrib( data, 'method', g.method, 'pairing', g.paired);
            end
            return;
        end

        % text output
        % -----------
        myfprintf(verb,'%d x %d, ', size(data,1), size(data,2));
        if strcmpi(g.paired, 'on')
             myfprintf(verb,'paired data, ');
        else myfprintf(verb,'unpaired data, ');
        end
        if size(data,1) == 1 && size(data,2) == 2
             myfprintf(verb,'computing T values\n');
        else myfprintf(verb,'computing F values\n');
        end
        if size(data,1) > 1
            if strcmpi(g.paired, 'on')
                 myfprintf(verb,'Using 2-way repeated measure ANOVA\n');
            else myfprintf(verb,'Using balanced 2-way ANOVA (not suitable for parametric testing, only bootstrap)\n');
            end
        elseif size(data,2) > 2
            if strcmpi(g.paired, 'on')
                 myfprintf(verb,'Using 1-way repeated measure ANOVA\n');
            else myfprintf(verb,'Using balanced 1-way ANOVA (equivalent to Matlab anova1)\n');
            end
        else
            if strcmpi(g.paired, 'on')
                 myfprintf(verb,'Using paired t-test\n');
            else myfprintf(verb,'Using unpaired t-test\n');
            end
        end
        if ~strcmpi(g.method, 'param')
            if bootflag, myfprintf(verb,'Bootstraps (of %d):', g.naccu);
            else         myfprintf(verb,'Permutations (of %d):', g.naccu);
            end
        end
    end

    tail = g.tail;
    if isempty(g.surrog)
        if size(data,1) == 1 % only one row

            if size(data,2) == 2 && strcmpi(g.forceanova, 'off')

                % paired t-test (very fast)
                % -------------
                [ori_vals, df] = ttest_cell_select(data, g.paired, g.variance);

                if strcmpi(g.method, 'param')

                    % Check if exist tcd.m file from the Statistics Toolbox (Bug 1352 )
                    if exist('tcdf','file') == 2  && license('test', 'Statistics_Toolbox')
                        pvals = 2*tcdf(-abs(ori_vals), df);
                    else
                        pvals = 2*mytcdf(-abs(ori_vals), df);
                    end

                    pvals = reshape(pvals, size(pvals));
                else
                    if strcmpi(g.arraycomp, 'on')
                        try
                            myfprintf(verb,'...');
                            res = surrogdistrib( data, 'method', g.method, 'pairing', g.paired, 'naccu', g.naccu);
                            surrogval = ttest_cell_select( res, g.paired, g.variance);
                        catch
                           lasterr
                           myfprintf(verb,'\nFast computation failed because of memory limitation, reverting to standard computation');
                           g.arraycomp = 'off';
                        end
                    end
                    if strcmpi(g.arraycomp, 'off')
                        [res, precomp] = surrogdistrib( data, 'method', g.method, 'pairing', g.paired);
                        for index = 1:g.naccu
                            res = surrogdistrib( {}, 'precomp', precomp);
                            if mod(index, 10) == 0, myfprintf(verb,'%d ', index); end
                            if mod(index, 100) == 0, myfprintf(verb,'\n'); end
                            if myndims(res{1}) == 1
                                 surrogval(index)     = ttest_cell_select(res, g.paired, g.variance);
                            else surrogval(:,index)   = ttest_cell_select(res, g.paired, g.variance);
                            end
                        end
                    end
                end
            else
                % one-way ANOVA (paired) this is equivalent to unpaired t-test
                % -------------
                tail = 'one';
                [ori_vals, df] = anova1_cell_select( data, g.paired );
                if strcmpi(g.method, 'param')
                    pvals = 1-fcdf(ori_vals, df(1), df(2));
                else
                    if strcmpi(g.arraycomp, 'on')
                        try
                            myfprintf(verb,'...');
                            res = surrogdistrib( data, 'method', g.method, 'pairing', g.paired, 'naccu', g.naccu);
                            surrogval = anova1_cell_select( res, g.paired );
                        catch,
                            myfprintf(verb,'\nFast computation failed because of memory limitation, reverting to standard computing');
                            g.arraycomp = 'off';
                        end
                    end
                    if strcmpi(g.arraycomp, 'off')
                        [res, precomp] = surrogdistrib( data, 'method', g.method, 'pairing', g.paired);
                        for index = 1:g.naccu
                            if mod(index, 10) == 0, myfprintf(verb,'%d ', index); end
                            if mod(index, 100) == 0, myfprintf(verb,'\n'); end

                            res = surrogdistrib( {}, 'precomp', precomp);
                            if myndims(data{1}) == 1
                            	 surrogval(index)     = anova1_cell_select( res, g.paired );
                            else surrogval(:,index)   = anova1_cell_select( res, g.paired );
                            end
                        end
                    end
                end
            end
        else
            % two-way ANOVA (paired or unpaired)
            % ----------------------------------
            tail = 'one';
            [ ori_vals{1}, ori_vals{2}, ori_vals{3}, df{1}, df{2}, df{3} ] = anova2_cell_select( data, g.paired );
            if strcmpi(g.method, 'param')
                pvals{1} = 1-fcdf(ori_vals{1}, df{1}(1), df{1}(2));
                pvals{2} = 1-fcdf(ori_vals{2}, df{2}(1), df{2}(2));
                pvals{3} = 1-fcdf(ori_vals{3}, df{3}(1), df{3}(2));
            else
                surrogval = { surrogval surrogval surrogval };
                dataori   = data;
                if strcmpi(g.arraycomp, 'on')
                    try
                        myfprintf(verb,'...');
                        res = surrogdistrib( data, 'method', g.method, 'pairing', g.paired, 'naccu', g.naccu);
                        [ surrogval{1}, surrogval{2}, surrogval{3} ] = anova2_cell_select( res, g.paired );
                    catch
                        myfprintf(verb,'\nFast computation failed because of memory limitation, reverting to standard computing');
                        g.arraycomp = 'off';
                    end
                end
                if strcmpi(g.arraycomp, 'off')
                    [res, precomp] = surrogdistrib( data, 'method', g.method, 'pairing', g.paired);
                    for index = 1:g.naccu
                        if mod(index, 10) == 0, myfprintf(verb,'%d ', index); end
                        if mod(index, 100) == 0, myfprintf(verb,'\n'); end

                        res = surrogdistrib( {}, 'precomp', precomp);
                        if myndims(data{1}) == 1
                         	 [ surrogval{1}(index),     surrogval{2}(index),     surrogval{3}(index)     ] = anova2_cell_select( res, g.paired );
                        else [ surrogval{1}(:,index),   surrogval{2}(:,index),   surrogval{3}(:,index)   ] = anova2_cell_select( res, g.paired );
                        end
                    end
                end
            end
        end
        myfprintf(verb,'\n');
    else
        surrogval = g.surrog;
        ori_vals  = g.stats;
        df        = [];
    end

    % compute p-values
    % ----------------
    if ~strcmpi(g.method, 'param')
        if iscell( surrogval )
            pvals{1} = stat_surrogate_pvals(surrogval{1}, ori_vals{1}, tail);
            pvals{2} = stat_surrogate_pvals(surrogval{2}, ori_vals{2}, tail);
            pvals{3} = stat_surrogate_pvals(surrogval{3}, ori_vals{3}, tail);
        else
            if strcmpi(g.cluster, 'on')
                if isnan(g.alpha)
                    g.alpha = 0.05;
                    disp('Alpha value set to 0.05 automatically');
                end
                if size(surrogval,3) > 1 || size(surrogval,4) > 1 || isnan(g.alpha)
                    error('Cluster method not implemented for alpha NaN or for more than 2 dims');
                else
                    tmpPvals = 2*tcdf(-abs(surrogval), size(surrogval,1)) < g.alpha;
                    largestCluster = zeros(1, size(surrogval,2));
                    signifPos = zeros(1, size(surrogval,1));
                    for iSurog = 1:size(surrogval,2)
                        signifPos = tmpPvals(:,iSurog);
                        while any(signifPos)
                            tmpInd = find(signifPos);
                            currentInd = tmpInd(1);
                            currentCount = 0;
                            while currentInd+currentCount <= length(signifPos) && signifPos(currentInd+currentCount) == 1
                                signifPos(currentInd+currentCount) = 0;
                                currentCount = currentCount+1;
                            end
                            if currentCount > largestCluster(iSurog), largestCluster(iSurog) = currentCount; end
                        end
                    end
                    largestCluster = sort(largestCluster);
                    thresholdVal = largestCluster(round(length(largestCluster)*(1-g.alpha)));

                    % assign p-val status
                    signifPos = 2*tcdf(-abs(ori_vals), length(ori_vals)) < g.alpha;
                    pvals     = ones(size(signifPos));
                    while any(signifPos)
                        tmpInd = find(signifPos);
                        currentInd = tmpInd(1);
                        currentCount = 0;
                        while currentInd+currentCount <= length(signifPos) && signifPos(currentInd+currentCount) == 1
                            signifPos(currentInd+currentCount) = 0;
                            currentCount = currentCount+1;
                        end
                        if currentCount >= thresholdVal, pvals(currentInd:currentInd+currentCount-1) = 0; end
                    end

                end
            else
                pvals = stat_surrogate_pvals(surrogval, ori_vals, tail);
            end
        end
        try, warning('on', 'MATLAB:divideByZero'); catch, end
    end

    [ ori_vals, pvals ] = reshape_results( nd, ori_vals, pvals);
    [ surrogval ]       = reshape_results( [nd g.naccu], surrogval);

    % confidence intervals
    % --------------------
    if ~isnan(g.alpha)
        outputstruct.ci = stat_surrogate_ci(surrogval, g.alpha, tail);
        if strcmpi(g.structoutput, 'off')
            disp('Warning: returning confidence interval requires an output structure');
        end
        if iscell(pvals)
            for ind = 1:length(pvals)
                outputstruct.mask{ind} = pvals{ind} < g.alpha;
            end
        else
            outputstruct.mask = pvals < g.alpha;
        end
    end

    % create a structure for outputting values
    % ---------------------------------------
    if strcmpi(g.structoutput, 'on')
        outputstruct.method = g.method;
        outputstruct.pval   = pvals;
        outputstruct.df     = df;
        outputstruct.surrog = surrogval;
        if length(data(:)) == 2
             outputstruct.t = ori_vals;
        else outputstruct.f = ori_vals;
        end
        outputstruct.stat   = ori_vals;
        ori_vals = outputstruct;
    end

% compute ANOVA 2-way
% -------------------
function [f1, f2, f3, df1, df2, df3] = anova2_cell_select( res, paired)
    if strcmpi(paired,'on')
        [f1, f2, f3, df1, df2, df3] = anova2rm_cell( res );
    else
        [f1, f2, f3, df1, df2, df3] = anova2_cell( res );
    end

% compute ANOVA 1-way
% -------------------
function [f, df] = anova1_cell_select( res, paired)
    if strcmpi(paired,'on')
        [f, df] = anova1rm_cell( res );
    else
        [f, df] = anova1_cell( res );
    end

% compute t-test
% -------------------
function [t, df] = ttest_cell_select( res, paired, homogenous)
    if strcmpi(paired,'on')
        [t, df] = ttest_cell( res{1}, res{2});
    else
        [t, df] = ttest2_cell( res{1}, res{2}, homogenous);
    end

% function to compute the number of dimensions
% --------------------------------------------
function val = myndims(a)
    if ndims(a) > 2
        val = ndims(a);
    else
        if size(a,1) == 1
            val = 2;
        elseif size(a,2) == 1
            val = 1;
        else
            val = 2;
        end
    end

% function for verbose messages
% -----------------------------
function myfprintf(verb, varargin)
    if verb
        fprintf(varargin{:});
    end

% function to replace tcdf
% ------------------------
function p = mytcdf(x,v)

if length(v) == 1
    v = repmat(v, size(x));
end

x2 = x.^2;
inds1 = (v < x2);
inds2 = (v >= x2);
if any(inds1(:)), p(inds1) = betainc(v(inds1) ./ (v(inds1) + x2(inds1)), v(inds1)/2, 0.5, 'lower') / 2; end
if any(inds2(:)), p(inds2) = betainc(x2(inds2) ./ (v(inds2) + x2(inds2)), 0.5, v(inds2)/2, 'upper') / 2; end
inds = (x > 0);
if any(inds)
    p(inds) = 1 - p(inds);
end

inds = (v > 1e7);
if any(inds(:)), p(inds) = normcum(x(inds)); end

p(x == 0) = 0.5;
if isempty(p)
    p = ones(size(x));
else
    p = reshape(p, size(x));
end
function [p] = normcum(z)
p = 0.5 * erfc(-z ./ sqrt(2));

% reshape results
% ---------------
function varargout = reshape_results(nd, varargin)
    if length(varargin) > 1
        for index = 1:length(varargin)
            varargout{index} = reshape_results(nd, varargin{index});
        end
    elseif iscell(varargin{1})
        for index = 1:length(varargin{1})
            varargout{1}{index} = reshape_results(nd, varargin{1}{index});
        end
    else
        if ~isempty(varargin{1})
            if length(nd) == 1, nd = [ nd 1 ]; end
            varargout{1} = reshape(varargin{1}, nd);
        else varargout{1} = [];
        end
    end


%==============================================================================
% This code is part of the Matlab-based toolbox
% FAIR - Flexible Algorithms for Image Registration.
% For details see
% - https://github.com/C4IR and
% - http://www.siam.org/books/fa06/
%==============================================================================
%
% Tutorial for FAIR: various distances versus rotation angle
%
%   - data                 PETCT, Omega=(0,140)x(0,151), level=4:7, m=[128,128]
%   - viewer               viewImage2D
%   - interpolation        splineInter
%   - distance             {'SSD','NCC','MI','NGF'}
%   - transformation       rotation2D
% see also E7_extended
%==============================================================================

clear, close all, help(mfilename);

fprintf('%s\n','setup data, viewer, interpolator, transformation model, distance')
setup2DPETCTData; level = 6; omega = ML{level}.omega; m = ML{level}.m;

viewImage('reset',viewPara{:},'axis','off');
imgModel('reset','imgModel','splineInter','regularizer','moments','theta',1e0);
[T,R] = imgModel('coefficients',ML{level}.T,ML{level}.R,omega,'out',0);
center = (omega(2:2:end)-omega(1:2:end))'/2;
trafo('reset','trafo','rotation2D','c',center);
distance('reset','distance','SSD');
fprintf('%20s : %s\n','viewImage',viewImage);
fprintf('%20s : %s\n','imgModel',imgModel);
fprintf('%20s : %s\n','trafo',trafo);

xc  = getCellCenteredGrid(omega,m);
Rc = imgModel(R,omega,xc);

distances = {'SSD','NCC','MI','NGF'};

for k=1:length(distances),
  distance('reset','distance',distances{k});
  fprintf('%20s : %s\n','distance',distance);
  wc = linspace(-pi/2,pi/2,49);
  Dc = zeros(size(wc));

  % run the loop over all rotations
  for j = 1:length(wc),
    yc    = trafo(wc(j),xc);         % compute transformed grid
    Tc    = imgModel(T,omega,yc);    % compute transformed image
    Dc(j) = distance(Tc,Rc,omega,m); % compute distance

    % visualize
    if j == 1,
      th = [];
      FAIRfigure(k,'figname',mfilename); clf;
      subplot(1,3,1);  viewImage(Rc,omega,m);            th(1) = title('R');
      subplot(1,3,2);  vh = viewImage(Tc,omega,m);       th(2) = title('T(yc)');
      subplot(1,3,3);  ph = plot(wc(1),Dc(1),'r.','markersize',20);
      th(3) = title(sprintf('%s versus rotation',distance));
      axis([wc(1),wc(end),-inf,inf]); hold on; set(th,'fontsize',30);
      axis('auto y');
      FAIRpause;
    else
      set(vh,'cdata',reshape(Tc,m)')
      subplot(1,3,3); set(ph,'visible','off');
      plot(wc(1:j),Dc(1:j),'k-','linewidth',2);
      ph = plot(wc(j),Dc(j),'r.','markersize',20);
      drawnow,
      FAIRpause(1/100)
    end;
    fprintf('.'); if ~rem(j,50) || j == length(wc), fprintf('\n'); end;
  end;
  FAIRpause;
end;

%==============================================================================


function rworlds = gen_random_obs(rows,cols,density,nworlds)
%
%Generated set of random obstacles to use to create random worlds
%
nobs = round((density/100)*rows*cols); %find number of obstacles
for i = 1:nworlds
    w(1:rows,1:cols) = -1;
    for j = 1:nobs
        tf = 1;
        while tf==1 %select empty location
            tr = round(1 + ( rows - 1) .* rand);
            tc = round(1 + ( cols - 1) .* rand);
            tf = 0;
            if w(tr,tc) == 1
                tf = 1;
            end
        end
        w(tr,tc) = 1;
        tdir = round(1 + ( 4 - 1) .* rand);%set a random direction
        switch tdir
            case 1
                td = 'N';
            case 2
                td = 'E';
            case 3
                td = 'W';
            case 4
                td = 'S';
        end
        tv = round((min(rows,cols)-1) .* rand);%set a random velocity
        rw(i).obs(j).r = tr;
        rw(i).obs(j).c = tc;
        rw(i).obs(j).vel = tv;
        rw(i).obs(j).dir = td;
    end
end
rworlds = rw;
end


function [L,yhat,st] = mci_pb_like (P,M,U,Y)
% Log-likelihood for Preece-Baines model
% FORMAT [L,yhat,st] = mci_pb_like (P,M,U,Y)
%
% P         parameters
% M,U,Y     as usual
%__________________________________________________________________________
% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging

% Will Penny
% $Id: mci_pb_like.m 6548 2015-09-11 12:39:47Z will $

% Status flag (only used for dynamic systems)
st=[];

T=length(Y);
yhat = mci_pb_gen (P,M,U);
E=sum(sum((Y-yhat).^2));

L = M.logdet_Ce - 0.5*T*log(2*pi);
L = L - 0.5*M.iCe*E;


function [ ap, info ] = dppfa ( ap, n )

%*****************************************************************************80
%
%% DPPFA factors a real symmetric positive definite matrix in packed form.
%
%  Discussion:
%
%    DPPFA is usually called by DPPCO, but it can be called
%    directly with a saving in time if RCOND is not needed.
%
%  Packed storage:
%
%    The following program segment will pack the upper
%    triangle of a symmetric matrix.
%
%      k = 0
%      do j = 1, n
%        do i = 1, j
%          k = k + 1
%          ap(k) = a(i,j)
%        end
%      end
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    17 June 2005
%
%  Author:
%
%    MATLAB version by John Burkardt.
%
%  Reference:
%
%    Dongarra, Moler, Bunch and Stewart,
%    LINPACK User's Guide,
%    SIAM, (Society for Industrial and Applied Mathematics),
%    3600 University City Science Center,
%    Philadelphia, PA, 19104-2688.
%    ISBN 0-89871-172-X
%
%  Parameters:
%
%    Input, real AP(N*(N+1)/2), the packed form of a symmetric matrix A.
%    The columns of the upper triangle are stored sequentially in a
%    one-dimensional array.
%
%    Input, integer N, the order of the matrix.
%
%    Output, real AP(N*(N+1)/2), an upper triangular matrix R, stored
%    in packed form, so that A = R'*R.
%
%    Output, integer INFO, error flag.
%    0, for normal return.
%    K, if the leading minor of order K is not positive definite.
%
  info = 0;
  jj = 0;

  for j = 1 : n

    s = 0.0;
    kj = jj;
    kk = 0;

    for k = 1 : j-1

      kj = kj + 1;
      t = ap(kj) - ddot ( k-1, ap(kk+1:kk+k-1), 1, ap(jj+1:jj+k-1), 1 );
      kk = kk + k;
      t = t / ap(kk);
      ap(kj) = t;
      s = s + t * t;

    end

    jj = jj + j;
    s = ap(jj) - s;

    if ( s <= 0.0 )
      info = j;
      return
    end

    ap(jj) = sqrt ( s );

  end

  return
end


function r8vec_sorted_unique_count_test ( )

%*****************************************************************************80
%
%% R8VEC_SORTED_UNIQUE_COUNT_TEST tests R8VEC_SORTED_UNIQUE_COUNT;
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 November 2005
%
%  Author:
%
%    John Burkardt
%
  n = 30;
  tol = 0.25;

  fprintf ( 1, '\n' );
  fprintf ( 1, 'R8VEC_SORTED_UNIQUE_COUNT_TEST\n' );
  fprintf ( 1, '  R8VEC_SORTED_UNIQUE_COUNT counts the unique entries\n' );
  fprintf ( 1, '  of a sorted R8VEC;\n' );

  b = 0.0;
  c = n;
  seed = 123456789;

  [ a, seed ] = r8vec_uniform_ab ( n, b, c, seed );

  a(1:n) = floor ( a(1:n) );

  unique_num = r8vec_sorted_unique_count ( n, a, tol );

  fprintf ( 1, '\n' );
  fprintf ( 1, '  Using a tolerance of %f\n', tol );
  fprintf ( 1, ...
    '  R8VEC_SORTED_UNIQUE_COUNT counts %d unique entries in A.\n', ...
    unique_num );

  return
end


function sol=polynsolve(polyset,ord,varnames,tol)
% POLYNSOLVE - attempt exact solution of multivariate polynomial system
%
% usage: sol=polynsolve(polyset)
%
% INPUTS: polyset, ord, varnames, tol
%  polyset is a cell array of polynomials in string or coefficient form
%    that is acceptable input for petschel.groebner.m
%  ord (optional) is the preferred ordering
%  varnames (optional) is the list of variable names if not {'x1','x2',...}
%  tol (optional) is the default zero tolerance
%    (see petschel.groebner.m for further details on ord, varnames, tol)
%
% OUTPUTS: sol
%  sol is an array containing the solutions to {polyset{:}=0}:
%    sol(i,j) is the value of xj in the i'th solution
%    If there are infinitely many solutions, sol(i,j)=NaN
%
% ALGORITHM:
%  Uses petschel.groebner bases (lex order).  If any one-variable polynomials
%  result, solve them and substitute back into the equations.
%  If "1" results, the system is not solvable.  If any multivariate polys
%  remain, those variables have an infinite number of solutions.
%
% KNOWN BUGS
%  See petschel.groebner.m for details
%
% SEE ALSO:
%  petschel.groebner, petschel.poly2str, petschel.str2poly

% Author: Ben Petschel 23/6/2009
%
% Change history:
%  23/6/2009 - first release
%  30/3/2010 - change poly representation from n-dim to rectangular array

if nargin<4

    tol = 0;

    if nargin<3

        varnames = {};

    end

end

if (nargin<2) || isempty(ord)

    ord = 'lex';

end

if (numel(polyset)>0) && ischar(polyset{1})

    polyset = petschel.str2poly(polyset,varnames);

end

gbasis=petschel.groebner(polyset,ord,varnames,tol);

% if petschel.groebner is all linear terms, it is a solution, otherwise find
% 1-variable polynomials and solve them, substituting the solutions into
% the other equations
sol = [];

i=1;

keepgoing = true;

while keepgoing && (i<=numel(gbasis))

    % search for 1-variable polynomials
    d = size(gbasis{i},2)-1;

    if numel(sol)<d
        % sol will always be a row vector, until possibly the final step
        % make sure sol has enough possible variables
        sol = [sol,nan(1,d-numel(sol))];

    end

    [tf,n,Q]=ispoly1(gbasis{i});

    if tf
        % attempt solution
        if length(Q)==1
            % have equation 1==0, so no solution
            sol = [];

            keepgoing = false;

        elseif length(Q)==2
            % Q=[a;b] so have equation a*xn+b=0 (a~=0) and solution is x=-b/a

            sol(n) = -Q(2)/Q(1);

        else
            % have polynomial in xn, so solve and substitute solution
            r = roots(Q);

            gbasisrecur = gbasis;

            sol = [];

            for j=1:length(r)
                % replace equation i with linear term (xn-r(j)==0)

                gbasisrecur{i} = linearterm(r(j),n);

                sol = [sol; petschel.polynsolve(gbasisrecur,ord,varnames,tol)];

                keepgoing = false;

            end

        end

    end

    i = i+1;

end

end % main function polynsolve(...)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [tf,n,Q]=ispoly1(P)
% returns tf=true if P is a 1-variable polynomial
% n is the number of the variable the polynomial is in
% Q is a column vector of the polynomial coefficients

if isempty(P) || all(P(:)==0)

    tf=true;

    n=1;

    Q=0;

elseif size(P,2)==1

    tf=true;

    n=1;

    Q=P;

else

    ind = find(any(P(:,2:end)>0,1));

    if numel(ind)==1
        % is a polynomial in 1 variable if exponents of only 1 variable are >0
        tf=true;

        n=ind;

        Q=[]; % collect coefficients of the polynomial

        Q(P(:,ind+1)+1)=P(:,1);

        Q=Q(end:-1:1); % reverse order to be consistent with ROOTS

    else

        tf=false;

        n=0;

        Q=[];

    end

end

end % helper function ispoly1(...)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Q=linearterm(a,n)
% returns coefficient array of a linear term (xn-a)

Q=zeros(2,n+1);

Q(1,1)=-a;

Q(2,1)=1;

Q(2,end)=1;

end % helper function linearterm(...)


function C = modwt_brick_wall(X, wavelet, N)
%%
%% Purpose:  Sets the first N_j coefficients to NaN, for j = 1,...,J.
%% -------------------------------------------------------------------------
%% Reference: Lindsay et al. (1996).  The Discrete Wavelet Transform
%%            and the Scale Anlaysis of the Surface Properties of Sea
%%            Ice.  IEEE Trans. on Geo. and Rem. Sen., 34(3), pp.
%%            771-787.
%%
%% Input: X        Matrix containing wavelet coefficients with appropriate
%%                 boundary condition
%%        wavelet  Character string; 'haar', 'd4', 'la8', 'la16'
%%        N        Length of original vector of observations
%%
%% Output: C  Matrix containing wavelet coefficients where ones affected
%%            by boundary conditions are replaced with NaNs
%%
[h, g, l] = myfilter(wavelet);
[I, J] = size(X);

C = X;
for j = 1:(J-1)
  n = (2.^j - 1) * (l - 1);
  C(1:n,j) = NaN;
end
C(1:n,j+1) = NaN;


function plotAcrobot(t,z,u,p)

[energy.total, energy.potential, energy.kinetic] = acrobotEnergy(z,p);

subplot(2,3,1);
plot(t,z(1,:))
xlabel('t')
ylabel('q1')
title('link one angle')
subplot(2,3,2);
plot(t,z(2,:))
xlabel('t')
ylabel('q2')
title('link two angle')
subplot(2,3,4);
plot(t,z(3,:))
xlabel('t')
ylabel('dq1')
title('link one rate')
subplot(2,3,5);
plot(t,z(4,:))
xlabel('t')
ylabel('dq2')
title('link two rate')

subplot(2,3,3); hold on
plot(t,energy.total,'k')
plot(t,energy.potential,'r')
plot(t,energy.kinetic,'b')
legend('total','potential','kinetic')
xlabel('t')
ylabel('e');
title('mechanical energy')

subplot(2,3,6)
plot(t,u)
xlabel('t')
ylabel('u')
title('torque between links')

end


function [Population,FrontNo,DWeight] = EnvironmentalSelection(Population,N)
% The environmental selection of DWU

%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

% This function is written by Gladston Moreira

    %% Non-dominated sorting
    [FrontNo] = NDSort(Population.objs,Population.cons,N);

    %% Calculate the dominance information each solution
    DWeight = InfoDominance(Population.objs);

    %% Environment Select
    Next = ReplacementUniformity(Population,N,FrontNo,DWeight);

    %% Population for next generation
    Population = Population(Next);
    FrontNo    = FrontNo(Next);
    DWeight    = DWeight(Next);
end

function InfoD = InfoDominance(PopObj)
% Calculate the information dominance each solution

    N = size(PopObj,1);

    %% Dominance count each solution
    D = false(N);
    for i = 1 : N-1
        for j = i+1 : N
            k = any(PopObj(i,:)<PopObj(j,:)) - any(PopObj(i,:)>PopObj(j,:));
            if k == 1
                D(i,j) = true;
            elseif k == -1
                D(j,i) = true;
            end
        end
    end
    CountDominance = sum(D,2);

    %% Calculate information dominance each solution
    InfoD = D'*CountDominance;
end


function plot_boundary_orange(V,F)
%PLOT_BOUNDARY_ORANGE  plots the boundary vertices of a triangle mesh in
%orange, and its interior in blue.
%
% plot_boundary_orange(V,F)(V,F);
%
% Input:
%  V,F  mesh whose boundary is to be plotted

%Compute a function u that is 0 on all interior vertices and 1 on all
%boundary vertices.
u = ...

%Plot the function u with the right colors.
t = tsurf(F,V, 'CData',u);
shading interp;
axis equal;
axis off;
cm = flipud(cbrewer('RdYlBu', 500));
colormap(cm(100:450,:));
light('Position',[-1.5 1 1],'Style','local');
lights = camlight;
set(t, 'FaceLighting','gouraud', 'FaceColor','interp');
set(t, 'DiffuseStrength',0.5, 'SpecularStrength',0.2, 'AmbientStrength',0.3);
camproj('perspective');
add_shadow([t],lights);

end


clc, clear, close all;

%% create data and query
p = rand( 1000, 2 );
tree = kdtree_build(p);
range = [ min( p(1:5,:) ); max( p(1:5,:) ) ]';
idxs = kdtree_range_query( tree, range );

%% visualize
hold on; xlim( [0 1] ); ylim( [0 1] ); axis equal;
plot(p(:,1),p(:,2), '.b');
plot(p(idxs,1), p(idxs,2), 'or');
legend('database', 'range query result');
line( range(1,[1,2]), range(2,[1,1]) ); % lower
line( range(1,[1,2]), range(2,[2,2]) ); % upper
line( range(1,[1,1]), range(2,[1,2]) ); % left
line( range(1,[2,2]), range(2,[1,2]) ); % right


function [um] = m2um(m)
% Convert length from meters to micrometers or microns.
% Chad A. Greene 2012
um = m*1000000;


function[varargout]=transmaxdist(varargin)
%TRANSMAXDIST  Distributions of wavelet transform maxima in noise.
%
%   This function is part of 'element analysis' described in Lilly (2017),
%   "Element analysis: a wavelet-based method for analyzing time-localized
%   events in noisy time series", available at www.jmlilly.net.
%
%   [COUNT,BINS]=TRANSMAXDIST(GAMMA,BETA,ALPHA,FS,R,N,M) returns the
%   histogram of wavelet transform maxima magnitudes, for a length N time
%   series having spectral slope -2*ALPHA transformed at frequencies FS
%   using a (GAMMA,BETA) wavelet, based on a simulation having N*M points.
%
%   Here GAMMA, BETA, ALPHA, and R are all scalars, or are all arrays of
%   the same length as FS.  FS is a frequency array computed by MORSESPACE.
%
%   R is the ratio between each frequency FS and the next.  This will be
%   constant and greater than one when FS is computed by MORSESPACE.  As
%   as described in Appendix C of Lilly (2017), R=FS(n)./FS(n+1) for all n.
%
%   COUNT is the number of transform maxima observed at each frequency in
%   the magnitude bins BINS.  COUNT is a LENGTH(BINS) x LENGTH(FS) matrix.
%
%   Transform maxima values, as output in BINS, are normalized such that
%   the expected squared magnitude of the wavelet transform of noise occurs
%   at unity.  BINS thus corresponds to the normalized event magnitude.
%
%   TRANSMAXDIST works by simulating a vector whose statistical properties
%   mimic those of the wavelet transform and the four adjacted points, thus
%   avoiding the need to explicitly compute the transform.  The choice of
%   e.g. M=1000 simulates a transform 1000 times as long as time series of
%   interest, which itself is of length N.
%
%   [COUNT,BINS,RATE]=TRANSMAXDIST(...) also returns the RATE, the
%   normalized reversed cumulative density function.  RATE gives the
%   expected number of transform maxima occuring in a time series of length
%   N having a magnitude greater than the corresponding bin value.
%
%   [COUNT,BINS,RATE,SIGMA]=TRANSMAXDIST(...) also returns the theoretical
%   covariance matrix SIGMA from which the Monte Carlo simulations are
%   constructed.  SIGMA is an array of length 5 x 5 x LENGTH(FS).
%
%   Note that if the covariance matrix is not positive definite, as can
%   happen due to numerical complications for extreme BETA and GAMMA
%   choices, then COUNT and RATE will both consist entirely of NaNs.
%   _______________________________________________________________________
%
%   Additional options
%
%   TRANSMAXDIST(...,BINS) alternately uses BINS for the bin centers
%   instead of the default choice, which is set to LINSPACE(0,6,200)'.
%
%   By default, TRANSMAXDIST performs a simulation for each of the scale
%   frequencies in FS.  TRANSMAXDIST(...,'extrapolate') instead computes
%   the distribution only for the highest scale frequency, then
%   extrapolates these values to all other scale frequencies with a scaling
%   law.  GAMMA, BETA, ALPHA, and R must all be scalars in this case.
%
%   For details, see Lilly (2017).
%
%   See also MAXPROPS, TRANSMAX, ISOMAX, MAX2EDDY.
%
%   'transmaxdist --t' runs some tests.
%
%   Usage: [count,bins]=transmaxdist(ga,be,al,fs,r,N,M);
%          [count,bins,rate,sigma]=transmaxdist(ga,be,al,fs,r,N,M);
%          [count,bins,rate,sigma]=transmaxdist(ga,be,al,fs,r,N,M,'extrap');
%   __________________________________________________________________
%   This is part of JLAB --- type 'help jlab' for more information
%   (C) 2017 J.M. Lilly --- type 'help jlab_license' for details

if strcmp(varargin{1}, '--t')
    transmaxdist_test,return
end

%parstr='serial';
%
%   TRANSMAXDIST(...,'parallel') uses a PARFOR loop in the computation of
%   the covariance matrix SIGMA.  The default behavior is 'serial'.

str='all';
normstr='band';
for i=1:3
    if ischar(varargin{end})
        if strcmpi(varargin{end}(1:3),'all')||strcmpi(varargin{end}(1:3),'ext')
            str=varargin{end};
        elseif strcmpi(varargin{end}(1:3),'ser')||strcmpi(varargin{end}(1:3),'par')
            parstr=varargin{end};
        elseif strcmpi(varargin{end}(1:3),'ban')||strcmpi(varargin{end}(1:3),'ene')
            normstr=varargin{end};
        end
        varargin=varargin(1:end-1);
    end
end

gamma=varargin{1};
beta=varargin{2};
alpha=varargin{3};
fs=varargin{4};
r=varargin{5};
N=varargin{6};
M=varargin{7};
if length(varargin)==7
    bins=linspace(0,6,200)';
else
    bins=varargin{8}(:);
end

s=morsefreq(gamma,beta)./fs;

%Some error checking
if ~isscalar(gamma)
    lg=length(gamma);
    lb=length(beta);
    la=length(alpha);
    ls=length(s);
    lr=length(r);
    if ~(lg==lb&&lg==la&&lg==ls&&lg==lr)
        error('TRANSMAXDIST was expecting the first five input arguments to all be the same length.')
    end
    if strcmpi(str(1:3),'ext')&&(length(gamma)>1)
        error('Sorry, ALPHA, BETA, GAMMA, FS, and R must be scalars for ''extrapolate'' option.')
    end
else
    if length(s)>1
        ro=s(2:end)./s(1:end-1);
        if ~allall(abs(ro-r)<1e8)
            disp('Input value of R does not match that computed from FS.');
        end
    end
end

arrayify(s,r,gamma,beta,alpha);
Sigma=zeros(5,5,length(s));

%s,r,gamma,beta,alpha
%if strcmpi(parstr(1:3),'ser')
    for i=1:length(s)
        Sigma(:,:,i)=sigmamat1(s(i),r(i),gamma(i),beta(i),alpha(i),normstr);
    end
%elseif strcmpi(parstr(1:3),'par')
%    parfor i=1:length(s)
%        Sigma(:,:,i)=sigmamat1(s(i),r(i),gamma(i),beta(i),alpha(i),normstr);
%    end
%end

xeps=(randn(5,N*M)+1i*randn(5,N*M))./sqrt(2);%std(xeps(:))
n=zeros(length(bins),length(s));

if strcmpi(str(1:3),'all')
    for k=1:length(s)
        disp(['TRANSMAXDIST performing simulation ' int2str(k) ' of ' int2str(length(s)) '.'])
        try
           L=chol(Sigma(:,:,k),'lower');
        catch
           L=[];
        end

        if ~isempty(L)
            wsim=L*xeps;

            bool=(abs(wsim(1,:))>abs(wsim(2,:)));
            bool=bool&(abs(wsim(1,:))>abs(wsim(3,:)));
            bool=bool&(abs(wsim(1,:))>abs(wsim(4,:)));
            bool=bool&(abs(wsim(1,:))>abs(wsim(5,:)));

            wmax=wsim(1,bool);
            nk=hist(abs(wmax),bins);
            n(:,k)=nk';
        else
            n(:,k)=nan*ones(size(n(:,k)));
        end
    end
elseif strcmpi(str(1:3),'ext')
    L=chol(Sigma(:,:,1),'lower');
    wsim=L*xeps;

    bool=(abs(wsim(1,:))>abs(wsim(2,:)));
    bool=bool&(abs(wsim(1,:))>abs(wsim(3,:)));
    bool=bool&(abs(wsim(1,:))>abs(wsim(4,:)));
    bool=bool&(abs(wsim(1,:))>abs(wsim(5,:)));

    wmax=wsim(1,bool);
    nk=hist(abs(wmax),bins);
    n(:,1)=nk';
    for k=2:length(s)
        n(:,k)=n(:,1)*frac(s(1),s(k));
    end
end

n=n./M;
rate=cumsum(n,1,'reverse');

%rate=n;
%for i=1:size(n,2)
    %L=2*sqrt(2)*sqrt(gamma(i).*beta(i))./fs(i);
 %   rate(:,i)=cumsum(n(:,i),1,'reverse');
%end

%     for i=1:5
%         for j=1:5
%             Sigmahat(i,j)=vmean(wsim(i,:).*conj(wsim(j,:)),2);
%         end
%     end
%     Sigmahat=Sigmahat./vmean(squared(wsim(1,:)),2);

varargout{1}=n;
varargout{2}=bins;
varargout{3}=rate;
varargout{4}=Sigma;
%varargout{4}=Sigmahat;

function[Sigma]=sigmamat1(s,r,gamma,beta,alpha,normstr)

p={gamma,beta,alpha,normstr};
Sigma=zeros(5,5);

Sigma(1,:)=[xi(0,s,1,p) xi(1,s,1,p)  xi(-1,s,1,p)  xi(0,s,r,p)   xi(0,s,1/r,p)     ];
Sigma(2,:)=[nan         xi(0,s,1,p)  xi(-2,s,1,p)  xi(-1,s,r,p)  xi(-1,s,1/r,p)    ];
Sigma(3,:)=[nan         nan          xi(0,s,1,p)   xi(1,s,r,p)   xi(1,s,1/r,p)     ];
Sigma(4,:)=[nan         nan          nan           xi(0,r*s,1,p) xi(0,r*s,1/r.^2,p)];
Sigma(5,:)=[nan         nan          nan           nan           xi(0,s/r,1,p)     ];

for j=1:5
    for i=(j+1):5
          Sigma(i,j)=conj(Sigma(j,i));
    end
end

[m0,ffun]=morsemom(-2*alpha,gamma,beta);
if strcmpi(normstr(1:3),'ene')
    Sigma=frac(Sigma,ffun.*s.^(2*alpha));
elseif strcmpi(normstr(1:3),'ban')
    Sigma=frac(Sigma,ffun*s.^(2*alpha-1));
end

function[x]=xi(tau,s,r,p)

gamma=p{1};
beta=p{2};
alpha=p{3};
normstr=p{4};

rtilde=(1+r.^gamma).^(1./gamma);

%Implicitly set A^2=1 as I will shortly divide by it
%fact1=frac(morseafun(gamma,beta).^2,morseafun(gamma,2*beta-2*alpha));
%fact2=frac((r.^beta).*(s.^(2*alpha-1)),rtilde.^(2*beta-2*alpha+1));

%Note:  use the input normalization in the numerator, and the *amplitude*
%normalization in the denominator.  All the latter does is cancel the
%coefficient coming from MORSEXPAND, which assumes the amplitude normalization
fact1=frac(morseafun(gamma,beta,normstr).^2,morseafun(gamma,2*beta-2*alpha));
if strcmpi(normstr(1:3),'ene')
    fact2=frac((r.^(beta+1/2)).*(s.^(2*alpha)),rtilde.^(2*beta-2*alpha+1));
elseif strcmpi(normstr(1:3),'ban')
    fact2=frac((r.^beta).*(s.^(2*alpha-1)),rtilde.^(2*beta-2*alpha+1));
end

x=fact1.*fact2.*conj(morsexpand(tau./(s.*rtilde),gamma,2*beta-2*alpha,morsefreq(gamma,2*beta-2*alpha)));
%x=fact1.*fact2.*conj(morsexpand(tau./(s.*rtilde),gamma,2*beta-2*alpha,morsefreq(gamma,2*beta-2*alpha),'cumulant'));

%fact1,fact2
%Equivalent
%fact2=frac((r.^beta).*(s.^(2*alpha)),rtilde.^(2*beta-2*alpha));
%fs=morsefreq(gamma,2*beta-2*alpha)./(s.*rtilde);
%x=fact1.*fact2.*conj(morsexpand(tau,gamma,2*beta-2*alpha,fs));

function[]=transmaxdist_test

for k=1:2
    tic
    ga=2;be=2;
    switch k
        case 1
            alpha=0;
        case 2
            alpha=1;
    end
    %fs=morsespace(ga,be,100);
    fs=morsespace(ga,be,{0.01,pi},2*pi/100);
    N=1e6;
    rng(1);
    x=randn(N,1);
    if alpha ==1
        x=cumsum(x);
        x=x./std(x);
    end
    w=wavetrans(x,{ga,be,fs(1:3),'bandpass'});

    clear xvec
    xvec(:,1)=w(:,2);
    xvec(:,2)=w([2:end 1],2);
    xvec(:,3)=w([end 1:end-1],2);
    xvec(:,4)=w(:,3);
    xvec(:,5)=w(:,1);

    for i=1:5
        for j=1:5
            Sigmahat(i,j)=vmean(xvec(:,i).*conj(xvec(:,j)),1);
        end
    end
    Sigmahat=Sigmahat./vmean(squared(xvec(:,1)),1);

    [count,bins,rate,Sigma]=transmaxdist(ga,be,alpha,fs(2),fs(1)./fs(2),N,1);
    switch k
        case 1
            bool=maxmax(abs(Sigma-Sigmahat)./abs(Sigma))<1/100;
            reporttest('TRANSMAXDIST simulated and theoretical covariance matrices agree to within 1%, white noise case',bool)
        case 2
            bool=maxmax(abs(Sigma-Sigmahat)./abs(Sigma))<4/100;
            reporttest('TRANSMAXDIST simulated and theoretical covariance matrices agree to within 4%, red noise case',bool)
    end

    %[index,ww]=transmax(fs(1:3),w);
    %n=hist(abs(ww)./vstd(w(:,2),1),bins)';
    %figure,plot(bins,[count n]) %That looks great!
end

function[]=transmaxdist_other

%[n,sigma]=transmaxdist(ga,be,0,[fs(2) fs(3)],N/10);
N=1e8;
tic;[n,x,Sigma]=transmaxdist(ga,be,0,fs,N);toc
for i=1:size(n,2)
    ntilde(:,i)=n(:,i)./frac(N,2*sqrt(2)*sqrt(ga*be)./fs(i));
    %ntilde(:,i)=n(:,i)./sum(n(:,i));
end

tic
N=1e8;
x=randn(N,1);
w=wavetrans(x,{ga,be,fs(1:3)});
toc

tic; [ii,jj,ww,ff]=transmax(fs(1:3),w);toc
bins=linspace(0,6,200)';
wwtilde=ww./sqrt(vmean(squared(w(:,2)),1));
nn=hist(abs(wwtilde),bins);
nn=nn./frac(N,2*sqrt(2)*sqrt(ga*be)./fs(2));