function [PSNR, y_est] = BM3D(y, z, sigma, profile, print_to_screen)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  BM3D is an algorithm for attenuation of additive white Gaussian noise from
%  grayscale images. This algorithm reproduces the results from the article:
%
%  [1] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "Image Denoising
%      by Sparse 3D Transform-Domain Collaborative Filtering,"
%      IEEE Transactions on Image Processing, vol. 16, no. 8, August, 2007.
%      preprint at http://www.cs.tut.fi/~foi/GCF-BM3D.
%
%
%  FUNCTION INTERFACE:
%
%  [PSNR, y_est] = BM3D(y, z, sigma, profile, print_to_screen)
%
%  ! The function can work without any of the input arguments,
%   in which case, the internal default ones are used !
%
%  BASIC USAGE EXAMPLES:
%
%     Case 1) Using the default parameters (i.e., image name, sigma, etc.)
%
%      [PSNR, y_est] = BM3D;
%
%     Case 2) Using an external noisy image:
%
%      % Read a grayscale image and scale its intensities in range [0,1]
%      y = im2double(imread('Cameraman256.png'));
%      % Generate the same seed used in the experimental results of [1]
%      randn('seed', 0);
%      % Standard deviation of the noise --- corresponding to intensity
%      %  range [0,255], despite that the input was scaled in [0,1]
%      sigma = 25;
%      % Add the AWGN with zero mean and standard deviation 'sigma'
%      z = y + (sigma/255)*randn(size(y));
%      % Denoise 'z'. The denoised image is 'y_est', and 'NA = 1' because
%      %  the true image was not provided
%      [NA, y_est] = BM3D(1, z, sigma);
%      % Compute the putput PSNR
%      PSNR = 10*log10(1/mean((y(:)-y_est(:)).^2))
%      % show the noisy image 'z' and the denoised 'y_est'
%      figure; imshow(z);
%      figure; imshow(y_est);
%
%     Case 3) If the original image y is provided as the first input
%      argument, then some additional information is printed (PSNRs,
%      figures, etc.). That is, "[NA, y_est] = BM3D(1, z, sigma);" in the
%      above code should be replaced with:
%
%      [PSNR, y_est] = BM3D(y, z, sigma);
%
%
%  INPUT ARGUMENTS (OPTIONAL):
%
%     1) y (matrix M x N): Noise-free image (needed for computing PSNR),
%                           replace with the scalar 1 if not available.
%     2) z (matrix M x N): Noisy image (intensities in range [0,1] or [0,255])
%     3) sigma (double)  : Std. dev. of the noise (corresponding to intensities
%                          in range [0,255] even if the range of z is [0,1])
%     4) profile (char)  : 'np' --> Normal Profile
%                          'lc' --> Fast Profile
%     5) print_to_screen : 0 --> do not print output information (and do
%                                not plot figures)
%                          1 --> print information and plot figures
%
%  OUTPUTS:
%     1) PSNR (double)          : Output PSNR (dB), only if the original
%                                 image is available, otherwise PSNR = 0
%     2) y_est (matrix M x N): Final estimate (in the range [0,1])
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Copyright (c) 2006-2011 Tampere University of Technology.
% All rights reserved.
% This work should only be used for nonprofit purposes.
%
% AUTHORS:
%     Kostadin Dabov, email: dabov _at_ cs.tut.fi
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% In case, a noisy image z is not provided, then use the filename
%%%%  below to read an original image (might contain path also). Later,
%%%%  artificial AWGN noise is added and this noisy image is processed
%%%%  by the BM3D.
%%%%
image_name = [
%     'montage.png'
     'Cameraman256.png'
%     'boat.png'
%     'Lena512.png'
%     'house.png'
%     'barbara.png'
%     'peppers256.png'
%     'fingerprint.png'
%     'couple.png'
%     'hill.png'
%     'man.png'
    ];

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%  Quality/complexity trade-off profile selection
%%%%
%%%%  'np' --> Normal Profile (balanced quality)
%%%%  'lc' --> Low Complexity Profile (fast, lower quality)
%%%%
%%%%  'high' --> High Profile (high quality, not documented in [1])
%%%%
%%%%  'vn' --> This profile is automatically enabled for high noise
%%%%           when sigma > 40
%%%%
%%%%  'vn_old' --> This is the old 'vn' profile that was used in [1].
%%%%           It gives inferior results than 'vn' in most cases.
%%%%
if (exist('profile') ~= 1)
    profile         = 'np'; %% default profile
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%  Specify the std. dev. of the corrupting noise
%%%%
if (exist('sigma') ~= 1),
    sigma               = 25; %% default standard deviation of the AWGN
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Following are the parameters for the Normal Profile.
%%%%

%%%% Select transforms ('dct', 'dst', 'hadamard', or anything that is listed by 'help wfilters'):
transform_2D_HT_name     = 'bior1.5'; %% transform used for the HT filt. of size N1 x N1
transform_2D_Wiener_name = 'dct';     %% transform used for the Wiener filt. of size N1_wiener x N1_wiener
transform_3rd_dim_name   = 'haar';    %% transform used in the 3-rd dim, the same for HT and Wiener filt.

%%%% Hard-thresholding (HT) parameters:
N1                  = 8;   %% N1 x N1 is the block size used for the hard-thresholding (HT) filtering
Nstep               = 3;   %% sliding step to process every next reference block
N2                  = 16;  %% maximum number of similar blocks (maximum size of the 3rd dimension of a 3D array)
Ns                  = 39;  %% length of the side of the search neighborhood for full-search block-matching (BM), must be odd
tau_match           = 3000;%% threshold for the block-distance (d-distance)
lambda_thr2D        = 0;   %% threshold parameter for the coarse initial denoising used in the d-distance measure
lambda_thr3D        = 2.7; %% threshold parameter for the hard-thresholding in 3D transform domain
beta                = 2.0; %% parameter of the 2D Kaiser window used in the reconstruction

%%%% Wiener filtering parameters:
N1_wiener           = 8;
Nstep_wiener        = 3;
N2_wiener           = 32;
Ns_wiener           = 39;
tau_match_wiener    = 400;
beta_wiener         = 2.0;

%%%% Block-matching parameters:
stepFS              = 1;  %% step that forces to switch to full-search BM, "1" implies always full-search
smallLN             = 'not used in np'; %% if stepFS > 1, then this specifies the size of the small local search neighb.
stepFSW             = 1;
smallLNW            = 'not used in np';
thrToIncStep        = 8;  % if the number of non-zero coefficients after HT is less than thrToIncStep,
                          % than the sliding step to the next reference block is incresed to (nm1-1)

if strcmp(profile, 'lc') == 1,

    Nstep               = 6;
    Ns                  = 25;
    Nstep_wiener        = 5;
    N2_wiener           = 16;
    Ns_wiener           = 25;

    thrToIncStep        = 3;
    smallLN             = 3;
    stepFS              = 6*Nstep;
    smallLNW            = 2;
    stepFSW             = 5*Nstep_wiener;

end

% Profile 'vn' was proposed in
%  Y. Hou, C. Zhao, D. Yang, and Y. Cheng, 'Comment on "Image Denoising by Sparse 3D Transform-Domain
%  Collaborative Filtering"', accepted for publication, IEEE Trans. on Image Processing, July, 2010.
% as a better alternative to that initially proposed in [1] (which is currently in profile 'vn_old')
if (strcmp(profile, 'vn') == 1) | (sigma > 40),

    N2                  = 32;
    Nstep               = 4;

    N1_wiener           = 11;
    Nstep_wiener        = 6;

    lambda_thr3D        = 2.8;
    thrToIncStep        = 3;
    tau_match_wiener    = 3500;
    tau_match           = 25000;

    Ns_wiener           = 39;

end

% The 'vn_old' profile corresponds to the original parameters for strong noise proposed in [1].
if (strcmp(profile, 'vn_old') == 1) & (sigma > 40),

    transform_2D_HT_name = 'dct';

    N1                  = 12;
    Nstep               = 4;

    N1_wiener           = 11;
    Nstep_wiener        = 6;

    lambda_thr3D        = 2.8;
    lambda_thr2D        = 2.0;
    thrToIncStep        = 3;
    tau_match_wiener    = 3500;
    tau_match           = 5000;

    Ns_wiener           = 39;

end

decLevel = 0;        %% dec. levels of the dyadic wavelet 2D transform for blocks (0 means full decomposition, higher values decrease the dec. number)
thr_mask = ones(N1); %% N1xN1 mask of threshold scaling coeff. --- by default there is no scaling, however the use of different thresholds for different wavelet decompoistion subbands can be done with this matrix

if strcmp(profile, 'high') == 1, %% this profile is not documented in [1]

    decLevel     = 1;
    Nstep        = 2;
    Nstep_wiener = 2;
    lambda_thr3D = 2.5;
    vMask = ones(N1,1); vMask((end/4+1):end/2)= 1.01; vMask((end/2+1):end) = 1.07; %% this allows to have different threhsolds for the finest and next-to-the-finest subbands
    thr_mask = vMask * vMask';
    beta         = 2.5;
    beta_wiener  = 1.5;

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Note: touch below this point only if you know what you are doing!
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%% Check whether to dump information to the screen or remain silent
dump_output_information = 1;
if (exist('print_to_screen') == 1) & (print_to_screen == 0),
    dump_output_information = 0;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Create transform matrices, etc.
%%%%
[Tfor, Tinv]   = getTransfMatrix(N1, transform_2D_HT_name, decLevel);     %% get (normalized) forward and inverse transform matrices
[TforW, TinvW] = getTransfMatrix(N1_wiener, transform_2D_Wiener_name, 0); %% get (normalized) forward and inverse transform matrices

if (strcmp(transform_3rd_dim_name, 'haar') == 1) | (strcmp(transform_3rd_dim_name(end-2:end), '1.1') == 1),
    %%% If Haar is used in the 3-rd dimension, then a fast internal transform is used, thus no need to generate transform
    %%% matrices.
    hadper_trans_single_den         = {};
    inverse_hadper_trans_single_den = {};
else
    %%% Create transform matrices. The transforms are later applied by
    %%% matrix-vector multiplication for the 1D case.
    for hpow = 0:ceil(log2(max(N2,N2_wiener))),
        h = 2^hpow;
        [Tfor3rd, Tinv3rd]   = getTransfMatrix(h, transform_3rd_dim_name, 0);
        hadper_trans_single_den{h}         = single(Tfor3rd);
        inverse_hadper_trans_single_den{h} = single(Tinv3rd');
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% 2D Kaiser windows used in the aggregation of block-wise estimates
%%%%
if beta_wiener==2 & beta==2 & N1_wiener==8 & N1==8 % hardcode the window function so that the signal processing toolbox is not needed by default
    Wwin2D = [ 0.1924    0.2989    0.3846    0.4325    0.4325    0.3846    0.2989    0.1924;
        0.2989    0.4642    0.5974    0.6717    0.6717    0.5974    0.4642    0.2989;
        0.3846    0.5974    0.7688    0.8644    0.8644    0.7688    0.5974    0.3846;
        0.4325    0.6717    0.8644    0.9718    0.9718    0.8644    0.6717    0.4325;
        0.4325    0.6717    0.8644    0.9718    0.9718    0.8644    0.6717    0.4325;
        0.3846    0.5974    0.7688    0.8644    0.8644    0.7688    0.5974    0.3846;
        0.2989    0.4642    0.5974    0.6717    0.6717    0.5974    0.4642    0.2989;
        0.1924    0.2989    0.3846    0.4325    0.4325    0.3846    0.2989    0.1924];
    Wwin2D_wiener = Wwin2D;
else
    Wwin2D           = kaiser(N1, beta) * kaiser(N1, beta)'; % Kaiser window used in the aggregation of the HT part
    Wwin2D_wiener    = kaiser(N1_wiener, beta_wiener) * kaiser(N1_wiener, beta_wiener)'; % Kaiser window used in the aggregation of the Wiener filt. part
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% If needed, read images, generate noise, or scale the images to the
%%%% [0,1] interval
%%%%
if (exist('y') ~= 1) | (exist('z') ~= 1)
    y        = im2double(imread(image_name));  %% read a noise-free image and put in intensity range [0,1]
    randn('seed', 0);                          %% generate seed
    z        = y + (sigma/255)*randn(size(y)); %% create a noisy image
else  % external images

    image_name = 'External image';

    % convert z to double precision if needed
    z = double(z);

    % convert y to double precision if needed
    y = double(y);

    % if z's range is [0, 255], then convert to [0, 1]
    if (max(z(:)) > 10), % a naive check for intensity range
        z = z / 255;
    end

    % if y's range is [0, 255], then convert to [0, 1]
    if (max(y(:)) > 10), % a naive check for intensity range
        y = y / 255;
    end
end

if (size(z,3) ~= 1) | (size(y,3) ~= 1),
    error('BM3D accepts only grayscale 2D images.');
end

% Check if the true image y is a valid one; if not, then we cannot compute PSNR, etc.
y_is_invalid_image = (length(size(z)) ~= length(size(y))) | (size(z,1) ~= size(y,1)) | (size(z,2) ~= size(y,2));
if (y_is_invalid_image),
    dump_output_information = 0;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Print image information to the screen
%%%%
if dump_output_information == 1,
    fprintf('Image: %s (%dx%d), sigma: %.1f\n', image_name, size(z,1), size(z,2), sigma);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Step 1. Produce the basic estimate by HT filtering
%%%%
tic;
y_hat = bm3d_thr(z, hadper_trans_single_den, Nstep, N1, N2, lambda_thr2D,...
	lambda_thr3D, tau_match*N1*N1/(255*255), (Ns-1)/2, (sigma/255), thrToIncStep, single(Tfor), single(Tinv)', inverse_hadper_trans_single_den, single(thr_mask), Wwin2D, smallLN, stepFS );
estimate_elapsed_time = toc;

if dump_output_information == 1,
    PSNR_INITIAL_ESTIMATE = 10*log10(1/mean((y(:)-double(y_hat(:))).^2));
    fprintf('BASIC ESTIMATE, PSNR: %.2f dB\n', PSNR_INITIAL_ESTIMATE);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Step 2. Produce the final estimate by Wiener filtering (using the
%%%%  hard-thresholding initial estimate)
%%%%
tic;
y_est = bm3d_wiener(z, y_hat, hadper_trans_single_den, Nstep_wiener, N1_wiener, N2_wiener, ...
    'unused arg', tau_match_wiener*N1_wiener*N1_wiener/(255*255), (Ns_wiener-1)/2, (sigma/255), 'unused arg', single(TforW), single(TinvW)', inverse_hadper_trans_single_den, Wwin2D_wiener, smallLNW, stepFSW, single(ones(N1_wiener)) );
wiener_elapsed_time = toc;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Calculate the final estimate's PSNR, print it, and show the
%%%% denoised image next to the noisy one
%%%%
y_est = double(y_est);

PSNR = 0; %% Remains 0 if the true image y is not available
if (~y_is_invalid_image), % checks if y is a valid image
    PSNR = 10*log10(1/mean((y(:)-y_est(:)).^2)); % y is valid
end

if dump_output_information == 1,
    fprintf('FINAL ESTIMATE (total time: %.1f sec), PSNR: %.2f dB\n', ...
        wiener_elapsed_time + estimate_elapsed_time, PSNR);

    figure, imshow(z); title(sprintf('Noisy %s, PSNR: %.3f dB (sigma: %d)', ...
        image_name(1:end-4), 10*log10(1/mean((y(:)-z(:)).^2)), sigma));

    figure, imshow(y_est); title(sprintf('Denoised %s, PSNR: %.3f dB', ...
        image_name(1:end-4), PSNR));

end

return;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Some auxiliary functions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels)
%
% Create forward and inverse transform matrices, which allow for perfect
% reconstruction. The forward transform matrix is normalized so that the
% l2-norm of each basis element is 1.
%
% [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels)
%
%  INPUTS:
%
%   N               --> Size of the transform (for wavelets, must be 2^K)
%
%   transform_type  --> 'dct', 'dst', 'hadamard', or anything that is
%                       listed by 'help wfilters' (bi-orthogonal wavelets)
%                       'DCrand' -- an orthonormal transform with a DC and all
%                       the other basis elements of random nature
%
%   dec_levels      --> If a wavelet transform is generated, this is the
%                       desired decomposition level. Must be in the
%                       range [0, log2(N)-1], where "0" implies
%                       full decomposition.
%
%  OUTPUTS:
%
%   Tforward        --> (N x N) Forward transform matrix
%
%   Tinverse        --> (N x N) Inverse transform matrix
%

if exist('dec_levels') ~= 1,
    dec_levels = 0;
end

if N == 1,
    Tforward = 1;
elseif strcmp(transform_type, 'hadamard') == 1,
    Tforward    = hadamard(N);
elseif (N == 8) & strcmp(transform_type, 'bior1.5')==1 % hardcoded transform so that the wavelet toolbox is not needed to generate it
    Tforward =  [ 0.353553390593274   0.353553390593274   0.353553390593274   0.353553390593274   0.353553390593274   0.353553390593274   0.353553390593274   0.353553390593274;
       0.219417649252501   0.449283757993216   0.449283757993216   0.219417649252501  -0.219417649252501  -0.449283757993216  -0.449283757993216  -0.219417649252501;
       0.569359398342846   0.402347308162278  -0.402347308162278  -0.569359398342846  -0.083506045090284   0.083506045090284  -0.083506045090284   0.083506045090284;
      -0.083506045090284   0.083506045090284  -0.083506045090284   0.083506045090284   0.569359398342846   0.402347308162278  -0.402347308162278  -0.569359398342846;
       0.707106781186547  -0.707106781186547                   0                   0                   0                   0                   0                   0;
                       0                   0   0.707106781186547  -0.707106781186547                   0                   0                   0                   0;
                       0                   0                   0                   0   0.707106781186547  -0.707106781186547                   0                   0;
                       0                   0                   0                   0                   0                   0   0.707106781186547  -0.707106781186547];
elseif (N == 8) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it
    Tforward = [ 0.353553390593274   0.353553390593274   0.353553390593274   0.353553390593274   0.353553390593274   0.353553390593274   0.353553390593274   0.353553390593274;
       0.490392640201615   0.415734806151273   0.277785116509801   0.097545161008064  -0.097545161008064  -0.277785116509801  -0.415734806151273  -0.490392640201615;
       0.461939766255643   0.191341716182545  -0.191341716182545  -0.461939766255643  -0.461939766255643  -0.191341716182545   0.191341716182545   0.461939766255643;
       0.415734806151273  -0.097545161008064  -0.490392640201615  -0.277785116509801   0.277785116509801   0.490392640201615   0.097545161008064  -0.415734806151273;
       0.353553390593274  -0.353553390593274  -0.353553390593274   0.353553390593274   0.353553390593274  -0.353553390593274  -0.353553390593274   0.353553390593274;
       0.277785116509801  -0.490392640201615   0.097545161008064   0.415734806151273  -0.415734806151273  -0.097545161008064   0.490392640201615  -0.277785116509801;
       0.191341716182545  -0.461939766255643   0.461939766255643  -0.191341716182545  -0.191341716182545   0.461939766255643  -0.461939766255643   0.191341716182545;
       0.097545161008064  -0.277785116509801   0.415734806151273  -0.490392640201615   0.490392640201615  -0.415734806151273   0.277785116509801  -0.097545161008064];
elseif (N == 8) & strcmp(transform_type, 'dst')==1 % hardcoded transform so that the PDE toolbox is not needed to generate it
    Tforward = [ 0.161229841765317   0.303012985114696   0.408248290463863   0.464242826880013   0.464242826880013   0.408248290463863   0.303012985114696   0.161229841765317;
       0.303012985114696   0.464242826880013   0.408248290463863   0.161229841765317  -0.161229841765317  -0.408248290463863  -0.464242826880013  -0.303012985114696;
       0.408248290463863   0.408248290463863                   0  -0.408248290463863  -0.408248290463863                   0   0.408248290463863   0.408248290463863;
       0.464242826880013   0.161229841765317  -0.408248290463863  -0.303012985114696   0.303012985114696   0.408248290463863  -0.161229841765317  -0.464242826880013;
       0.464242826880013  -0.161229841765317  -0.408248290463863   0.303012985114696   0.303012985114696  -0.408248290463863  -0.161229841765317   0.464242826880013;
       0.408248290463863  -0.408248290463863                   0   0.408248290463863  -0.408248290463863                   0   0.408248290463863  -0.408248290463863;
       0.303012985114696  -0.464242826880013   0.408248290463863  -0.161229841765317  -0.161229841765317   0.408248290463863  -0.464242826880013   0.303012985114696;
       0.161229841765317  -0.303012985114696   0.408248290463863  -0.464242826880013   0.464242826880013  -0.408248290463863   0.303012985114696  -0.161229841765317];
elseif strcmp(transform_type, 'dct') == 1,
    Tforward    = dct(eye(N));
elseif strcmp(transform_type, 'dst') == 1,
    Tforward    = dst(eye(N));
elseif strcmp(transform_type, 'DCrand') == 1,
    x = randn(N); x(1:end,1) = 1; [Q,R] = qr(x);
    if (Q(1) < 0),
        Q = -Q;
    end;
    Tforward = Q';
else %% a wavelet decomposition supported by 'wavedec'
    %%% Set periodic boundary conditions, to preserve bi-orthogonality
    dwtmode('per','nodisp');

    Tforward = zeros(N,N);
    for i = 1:N
        Tforward(:,i)=wavedec(circshift([1 zeros(1,N-1)],[dec_levels i-1]), log2(N), transform_type);  %% construct transform matrix
    end
end

%%% Normalize the basis elements
Tforward = (Tforward' * diag(sqrt(1./sum(Tforward.^2,2))))';

%%% Compute the inverse transform matrix
Tinverse = inv(Tforward);

return;


function [fDeltaProbability, fProbabilityN, fProbabilityH, mPredictionFMD, vObservedFMD, vMagnitudeBins, vProbH, vProbN] ...
        = pt_poissonian(mLearningCatalog, fLearningTime, mObservedCatalog, fObservedTime, nMinimumNumber, fBValueH, fBValueN, ...
        fMcH, fMcN, fMinMag, fMaxMag)
    % Calculation of likelihoods for two models based on a- and b-values
    %
    % [fDeltaProbability, fProbabilityN, fProbabilityH, mPredictionFMD, vObservedFMD, vMagnitudeBins, vProbH, vProbN]
    %   = pt_poissonian(mLearningCatalog, fLearningTime, mObservedCatalog, fObservedTime, nMinimumNumber, fBValueH, fBValueN,
    %                   fMcH, fMcN, fMinMag, fMaxMag);
    %
    %
    % Input parameters:
    %   mLearningCatalog    Earthquake catalog of the learning period
    %   fLearningTime       Length of learning period (can be different than the exact length of mLearningCatalog)
    %   mObservedCatalog    Earthquake catalog of the observed period
    %   fObservedTime       Length of observed period (can be different than the exact length of mObservedCatalog)
    %   nMinimumNumber      Minimum number of earthquakes in the catalog for calculating the output values
    %   fBValueH            b-value for test hypothesis
    %   fBValueN            b-value for null hypothesis
    %   fMcH                Magnitude of completeness for the test hypothesis
    %   fMcN                Magnitude of completeness for the null hypothesis
    %   fMinMag             Minimum magnitude for testing
    %   fMaxMag             Maximum magnitude for testing
    %
    % Output parameters:
    %   fDeltaProbability   Difference of the log-likelihood between both of the models
    %   fProbabilityN       Log-likelihood of the null hypothesis
    %   fProbabilityH       Log-likelihood of the test hypothesis
    %   mPredictionFMD      Forecasted FMD of both models (mPredictionFMD(:,1) test hypothesis, (:,2) null hypothesis)
    %   vObservedFMD        Vector with observations corresponding to mPredictionFMD
    %   vMAgnitudeBins      Vector containing the tested magnitude bins
    %   vProbH              log-likelihoods per magnitude bin of the test hypothesis
    %   vProbN              log-likelihoods per magnitude bin of the null hypothesis
    %
    % Danijel Schorlemmer
    % April 30, 2003

    report_this_filefun();

    % Index description
    % xxxxN : Variable with a value for the null hypothesis model
    % xxxxH : Variable with a value for the hypothesis model

    if ((length(mLearningCatalog(:,1)) < nMinimumNumber) | (isnan(fBValueH)) | (isnan(fBValueN)))
        fDeltaProbability = nan;
        fProbabilityH = nan;
        fProbabilityN = nan;
        mPredictionFMD = [];
        vObservedFMD = [];
        vMagnitudeBins = [];
        vProbH = [];
        vProbN =[];
    else
        % Select the higher magnitude of completeness
        fMc = max([fMcH fMcN]);
        % Cut the first catalog at magnitude of completeness
        vSel = mLearningCatalog(:,6) >= fMc;
        mCalcACatalog = mLearningCatalog(vSel,:);
        if length(mCalcACatalog(:,1)) >= nMinimumNumber
            % Compute the maximum likelihood a-values for both models
            fAValueH = calc_MaxLikelihoodA(mCalcACatalog, fBValueH);
            fAValueN = calc_MaxLikelihoodA(mCalcACatalog, fBValueN);
            % Create the predicted FMD
            vCnt = (fMinMag:0.1:fMaxMag+0.1)'; % Add one more magnitude bin for later use of diff()
            vNumberH = 10.^(fAValueH - (fBValueH * vCnt))/fLearningTime * fObservedTime;
            vNumberN = 10.^(fAValueN - (fBValueN * vCnt))/fLearningTime * fObservedTime;
            mNumbers = [vNumberH vNumberN];
            % Determine the number of events in each magnitude bin
            mPredictionFMD = -diff(mNumbers);
            % Cut the second catalog at the minimum magnitude for testing
            vSel = mObservedCatalog(:,6) >= fMinMag;
            mCalcCatalog = mObservedCatalog(vSel,:);
            % Create the FMD for the period of observation
            vObservedFMD = histogram(mCalcCatalog(:,6), fMinMag:0.1:fMaxMag);
            % Calculate the likelihoods for both of the models
            vProbH = calc_logpoisspdf(vObservedFMD', mPredictionFMD(:,1));
            vProbN = calc_logpoisspdf(vObservedFMD', mPredictionFMD(:,2));
            % Return the values
            fProbabilityH = sum(vProbH);
            fProbabilityN = sum(vProbN);
            fDeltaProbability = fProbabilityN - fProbabilityH;
            vObservedFMD = vObservedFMD';   % Simply for returning the vector in the same way as vPredictionFMD
            vMagnitudeBins = (fMinMag:0.1:fMaxMag)';
        else
            fDeltaProbability = nan;
            fProbabilityH = nan;
            fProbabilityN = nan;
            mPredictionFMD = [];
            vObservedFMD = [];
            vMagnitudeBins = [];
            vProbH = [];
            vProbN =[];
        end
    end
end


% Frequency response Magnet Driver circuit network using DS method
%  and G array.
% File:  c:\M_files\short_updates\UsingGarray1.m
% 02/10/07
% See Word file UsingGarray1.doc for background explanations.
clear;clc;
% unit suffixes
u=1e-6;K=1e3;m=1e-3;u=1e-6;p=1e-12;
% component values
R1=5*K;R2=1*K;R3=10*K;
C1=0.1*u;C2=400*p;
Ein=1; % Unity input for (normalized) transfer function
N=2; % Number of capacitors = order of circuit
%
% Form W, Q, S, and P arrays:
%
W=[1+R3/R1 1+R3/R1;R2 0];Q=[0 -1/R1;-1 1];S=[Ein/R1;0];P=diag([C1 C2]);
%
% Get A, B, D, & E arrays:
%
C=inv(W*P);A=C*Q;B=C*S;
% D & E from the first output equation:
D1=[0 R1/(R1+R3)];E1=R3/(R1+R3);
% D & E from the second (easier) output equation:
D2=[0 1];E2=0;
%
% Now we can form the G array = F*P as follows.
%
F=[R3 R3]; % R3 is the coefficient of both iC1 and iC2 in Vo2
% which are derivative terms (iC=dVc/dt, etc.)
G=F*P;
%
% * * * * * * * * * * * * Frequency response * * * * * * * * * * * *
%
I=eye(N); % identity matrix
%
% Log frequency sweep from BF to BF+ND
%
BF=0;ND=4;PD=25;NP=ND*PD+1;Fr=logspace(BF,BF+ND,NP);
for i=1:NP
   s=2*pi*Fr(i)*j; % j = sqrt(-1)
   stm=(s*I-A)\B;v1=abs(D1*stm+E1);Vo1(i)=20*log10(v1);
   v2=abs((D2+G*A)*stm+E2+G*B);Vo2(i)=20*log10(v2);
end
%
% Plot frequency (Vo1 & Vo2 separated by 0.2 dBV to ovoid overlay)
%
h=plot(log10(Fr),Vo1-0.2,'b',log10(Fr),Vo2,'r');
set(h,'LineWidth',2);
grid on;
axis auto
ylabel('dBV');title('Magnet Driver Output');
xlabel('Log Freq(Hz)');
legend('Vo1 - 0.2','Vo2 using G array');
figure(1);
%


function polygon_properties_test11 ( )

%*****************************************************************************80
%
%% POLYGON_PROPERTIES_TEST11 tests POLYGON_LATTICE_AREA.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    07 May 2014
%
%  Author:
%
%    John Burkardt
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'POLYGON_PROPERTIES_TEST11\n' );
  fprintf ( 1, '  POLYGON_LATTICE_AREA returns the "area"\n' );
  fprintf ( 1, '  of a polygon, measured in lattice points.\n' );

  i = 5;
  b = 6;

  fprintf ( 1, '\n' );
  fprintf ( 1, '  Number of interior lattice points = %d\n', i );
  fprintf ( 1, '  Number of boundary lattice points = %d\n', b );

  area = polygon_lattice_area ( i, b );

  fprintf ( 1, '  Area of polygon is %g\n', area );

  return
end


classdef SOP_F16 < PROBLEM
% <single> <real> <expensive/none>
% Six-hump camel-back function

%------------------------------- Reference --------------------------------
% X. Yao, Y. Liu, and G. Lin, Evolutionary programming made faster, IEEE
% Transactions on Evolutionary Computation, 1999, 3(2): 82-102.
%------------------------------- 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".
%--------------------------------------------------------------------------

    methods
        %% Default settings of the problem
        function Setting(obj)
            obj.M = 1;
            obj.D = 2;
            obj.lower    = zeros(1,obj.D) - 5;
            obj.upper    = zeros(1,obj.D) + 5;
            obj.encoding = ones(1,obj.D);
        end
        %% Calculate objective values
        function PopObj = CalObj(obj,PopDec)
            PopObj = 4*PopDec(:,1).^2-2.1*PopDec(:,1).^4+PopDec(:,1).^6/3+PopDec(:,1).*PopDec(:,2)-4*PopDec(:,2).^2+4*PopDec(:,2).^4;
        end
        %% Generate the minimum objective value
        function R = GetOptimum(obj,N)
            R = -1.0316;
        end
    end
end


function Y = invariantMultiply_new( A, X)
%
%           Y = invariantMultiply( A, X );
%
%  This function computes the multiplication of a spatially invariant
%  point spread function (PSF) times an image
%                y = A*x
%
%  Here we use a Kronecker product approach to better handle reflexive
%  boundary conditions
%
%  Input:
%            A  -  psfMatrix object
%            X  -  array containing the image to which the psfMatrix
%                  is to be multiplied.
%
%  Output:
%            Y  -  contains the result after PSF multiplication.
%

%  J. Nagy  7/5/2016

imsize = size( X );
psfMatData = A.matdata;

switch A.boundary
    case 'periodic'
        if A.transpose
            Y = real(ifft2(conj(psfMatData).*fft2(X)));
        else
            Y = real(ifft2(psfMatData.*fft2(X)));
        end
    case 'zero'
        p = A.p;
        padSize = [size(psfMatData,1),size(psfMatData,2)] - size(X);
        Xpad = padarray(X, padSize, 'post');
        Xtilde = fft2(Xpad);
        Xtilde = Xtilde(:,p(:,1));
        if A.transpose
            Y = conj(psfMatData(:,:,1)).*Xtilde;
        else
            Y = psfMatData(:,:,1).*Xtilde;
        end
        Y = real(ifft2(Y));
        Y = Y(:, p(:,1));
        Y = Y(1:imsize(1), 1:imsize(2));
    case 'reflexive'
        p = A.p;
        padSize = [size(psfMatData,1),size(psfMatData,2)] - size(X);
        Xpad = padarray(X, padSize, 'post');
        Xtilde = fft2(Xpad);
        Xtilde = Xtilde(:,p(:,1));
        if A.transpose
            term1 = conj(psfMatData(:,:,1)).*Xtilde;
            term2 = conj(psfMatData(:,:,2)).*Xtilde(:,p(:,1));
            term3 = conj(psfMatData(:,:,3)).*Xtilde(p(:,2),:);
            term4 = conj(psfMatData(:,:,4)).*Xtilde(p(:,2),p(:,1));
        else
            term1 = psfMatData(:,:,1).*Xtilde;
            term2 = psfMatData(:,:,2).*Xtilde;
            term2 = term2(:,p(:,1));
            term3 = psfMatData(:,:,3).*Xtilde;
            term3 = term3(p(:,2),:);
            term4 = psfMatData(:,:,4).*Xtilde;
            term4 = term4(p(:,2),p(:,1));
        end
        Y = real(ifft2(term1 + term2 + term3 + term4));
        Y = Y(:,p(:,1));
        Y = Y(1:imsize(1), 1:imsize(2));
    otherwise
        error('still working on this')
end


% function Z = ZG_rdiv(X,Y)
%
% row division: Z = X / Y row-wise
% Y must have one column
%
% Machine Learning Toolbox
% Version 1.0  01-Apr-96
% Copyright (c) by Zoubin Ghahramani
% http://mlg.eng.cam.ac.uk/zoubin/software.html
%
% ------------------------------------------------------------------------------
% The MIT License (MIT)
%
% Copyright (c) 1996, Zoubin Ghahramani
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
% of this software and associated documentation files (the "Software"), to deal
% in the Software without restriction, including without limitation the rights
% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
% copies of the Software, and to permit persons to whom the Software is
% furnished to do so, subject to the following conditions:
%
% The above copyright notice and this permission notice shall be included in
% all copies or substantial portions of the Software.
%
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
% THE SOFTWARE.
% ------------------------------------------------------------------------------

function Z = ZG_rdiv(X,Y)

if (length(X(:,1)) ~= length(Y(:,1))) || (length(Y(1,:)) ~= 1)
    error('Error in ZG_RDIV');
end

Z = zeros(size(X));

for i = 1:length(X(1,:))
    Z(:,i) = X(:,i)./Y;
end

end


function varargout = imGradient(img, varargin)
% Compute gradient magnitude of a grayscale image.
%
%   [GX, GY] = imGradient(IMG);
%   [GX, GY, GZ] = imGradient(IMG);
%   Compute the components of the gradient vector of a 2D or a 3D image.
%   The gradient is computed in each orthogonal direction by using
%   normalised Sobel filters.
%   For a 2D image, gradient magnitude and angle can be computed from:
%   GMAG = hypot(GX, GY);
%   GANGLE = atan2(GY, GX);
%
%   GRAD = imGradient(IMG);
%   Compute an image of gradient magnitude. Magnitude is computed as:
%   - hypot(dx, dy) for 2D images
%   - hypot(hypot(dx, dy), dz) for 3D images
%
%   GRAD = imGradient(IMG, SIGMA);
%   Specifies the width of the kernel (2D only). The size of the kernel is
%   determined automatically from the SIGMA.
%
%   GRAD = imGradient(IMG, 'filter', FILTER);
%   Specifies the filter used for computing gradient in the X direction.
%   Note that fspecial('sobel') or fspecial('prewitt') return unnormalized
%   filters for the Y direction (horizontal gradient).
%
%   ... = imGradient(..., OPTION1, OPTION2...);
%   Will use the given set of options when computing the gradient filter.
%   See the documentation of imfilter for details.
%   Default options are 'conv' and 'replicate'.
%
%   Example
%   % display edge strength computed on cameraman picture
%     img = imread('cameraman.tif');
%     grad = imGradient(img);
%     imshow(grad, [0 max(grad(:))]);  colormap parula;
%     % use larger parameter for sigma
%     grad5 = imGradient(img, 5);
%     figure; imshow(grad5, [0 max(grad5(:))]); colormap parula;
%
%   % compute edge direction on rice picture
%     img = imread('rice.png');
%     [dx, dy] = imGradient(img);
%     grad = hypot(dx, dy);
%     theta = atan2(dy, dx);
%     rgb = angle2rgb(theta);     % convert to floating-point rgb
%     bin = grad>20;              % select only salient edges
%     rgb(~bin(:,:, [1 1 1])) = 0;% display salient edges orientation
%     imshow(rgb);
%
%   % Uses a different filter (the same as for the "gradient" function)
%     img = imread('cameraman.tif');
%     grad = imGradient(img, 'filter', [1 0 -1]);
%     imshow(grad, [0 max(grad(:))]);
%
%   See also
%     imLaplacian, imMorphoGradient, imHessian
%     imfilter, fspecial, angle2rgb, gradient

% ------
% Author: David Legland
% e-mail: david.legland@inrae.fr
% Created: 2009-08-19,    using Matlab 7.7.0.471 (R2008b)
% Copyright 2009 INRA - Cepia Software Platform.

% HISTORY
% 2010-01-13 add support for gradient direction and filter options
% 2010-02-16 change output format for 2 output parameters, add support
%   for 3D images, and add psb to change filter.
% 2010-03-03 use convolution by default
% 2010-03-05 return result as double, normalize default filter
% 2010-12-06 use 3D kernel by default for 3D images
% 2013-05-20 add support for variable kernel width (2D only)

%% Parse input arguments

% image dimension
dim = size(img);

% number of dimension of image (do not manage color images)
nd = length(dim);

% check if the width of the kernel is specified
sigma = 0;
if ~isempty(varargin)
    var1 = varargin{1};
    if isnumeric(var1) && isscalar(var1)
        sigma = var1;
        varargin(1) = [];
    end
end

% default filter for gradient: normalised sobel
if nd <= 2
    sx = gradientKernels(sigma);
elseif nd == 3
    sx = gradientKernels3d(sigma);
else
    error('Input image must have 2 or 3 dimensions');
end

% check if another gradient filter is proposed
for i = 1:length(varargin)-1
    if strcmp(varargin{i}, 'filter')
        sx = varargin{i+1};
        varargin(i:i+1) = [];
        break;
    end
end

% default options for computations
varargin = [{'replicate'}, {'conv'}, varargin];

%% Gradient computation

% compute gradients in each main direction
if nd == 2
    % Process 2D Image
    dx = imfilter(double(img), sx, varargin{:});
    dy = imfilter(double(img), sx', varargin{:});

elseif nd == 3
    % Process 3D Image
    sy = permute(sx, [2 3 1]);
    sz = permute(sx, [3 1 2]);
    dx = imfilter(double(img), sx, varargin{:});
    dy = imfilter(double(img), sy, varargin{:});
    dz = imfilter(double(img), sz, varargin{:});
end

%% Format output

% Depending on number of output arguments, returns either the gradient
% module, or each component of the gradient vector.
if nargout == 1
    % compute gradient module
    if nd == 2
        varargout{1} = hypot(dx, dy);
    else
        varargout{1} = hypot(hypot(dx, dy), dz);
    end

else
    % return each component of the vector array
    varargout{1} = dx;
    varargout{2} = dy;
    if nd > 2
        varargout{3} = dz;
    end
end


function Y = ttm(X,V,varargin)
%TTM Sparse tensor times matrix.
%
%   Y = TTM(X,A,N) computes the n-mode product of the sptensor X with
%   a dense matrix A; i.e., X x_N A.  The integer N specifies the
%   dimension (or mode) of X along which A should be multiplied.  If
%   size(A) = [J,I], then X must have size(X,N) = I.  The result will
%   will be a (dense) tensor or sptensor of the same order and size as X
%   except that size(Y,N) = J.
%
%   Y = TTM(X,{A,B,C,...}) computes the n-mode product of the sptensor
%   X with a sequence of matrices in the cell array.  The n-mode
%   products are computed sequentially along all dimensions (or modes)
%   of X. The cell array contains ndims(X) matrices.
%
%   Y = TTM(X,{A,B,C,...},DIMS) computes the sequence tensor-matrix
%   products along the dimensions specified by DIMS.
%
%   Y = TTM(...,'t') performs the same computations as above except
%   the matrices are transposed.
%
%   Examples
%   X = sptenrand([5 3 4 2], 10);
%   A = rand(4,5); B = rand(4,3); C = rand(3,4); D = rand(3,2);
%   Y = ttm(X, A, 1)         %<-- computes X times A in mode-1
%   Y = ttm(X, {A,B,C,D}, 1) %<-- same as above
%   Y = ttm(X, A', 1, 't')   %<-- same as above
%   Y = ttm(X, {A,B,C,D}, [1 2 3 4]) %<-- 4-way multiply
%   Y = ttm(X, {D,C,B,A}, [4 3 2 1]) %<-- same as above
%   Y = ttm(X, {A,B,C,D})            %<-- same as above
%   Y = ttm(X, {A',B',C',D'}, 't')   %<-- same as above
%   Y = ttm(X, {C,D}, [3 4])     %<-- X times C in mode-3 & D in mode-4
%   Y = ttm(X, {A,B,C,D}, [3 4]) %<-- same as above
%   Y = ttm(X, {A,B,D}, [1 2 4])   %<-- 3-way multiply
%   Y = ttm(X, {A,B,C,D}, [1 2 4]) %<-- same as above
%   Y = ttm(X, {A,B,D}, -3)        %<-- same as above
%   Y = ttm(X, {A,B,C,D}, -3)      %<-- same as above
%
%   See also SPTENSOR, TENSOR/TTM.
%
%MATLAB Tensor Toolbox.
%Copyright 2012, Sandia Corporation.

% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2012) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt

%% Check the number of arguments
if (nargin < 2)
    error('TTM requires at least two arguments.');
end

%% Create 'n' and 'tflag' arguments from varargin
n = 1:ndims(X);
tflag = '';
if numel(varargin) == 1
    if ischar(varargin{1})
        tflag = varargin{1};
    else
        n = varargin{1};
    end
elseif numel(varargin) == 2
    n = varargin{1};
    tflag = varargin{2};
end

%% Handle cell array
if iscell(V)
    % Copy n into dims
    dims = n;
    % Check that the dimensions are valid
    [dims,vidx] = tt_dimscheck(dims,ndims(X),numel(V));
    % Calculate individual products
    Y = ttm(X, V{vidx(1)}, dims(1), tflag);
    for i = 2 : numel(dims)
        Y = ttm(Y, V{vidx(i)}, dims(i), tflag);
    end
    % All done
    return;
end

%% Check the second argument
if ndims(V) ~= 2
    error('tensor/ttm: 2nd argument must be a matrix.');
end

%% Flip V is transposed
if tflag == 't'
    V = V';
end

%% Check n
if numel(n) ~= 1 || (n < 0) || (n > ndims(X))
    error('Dimension N must be between 1 and NDIMS(X).');
end

%% Compute the product

% Check that sizes match!
if size(X,n) ~= size(V,2)
    error('Size mismatch on V');
end

% Compute the new size
siz = size(X);
siz(n) = size(V,1);

% Compute Xn'
Xnt = sptenmat(X,n,'t');

% Extract the dimensions
rdims = Xnt.rdims;
cdims = Xnt.cdims;

% Convert to sparse matrix and do the multiplication; result is generally a
% dense matrix
Z = double(Xnt) * V';

if nnz(Z) <= 0.5 * prod(siz)
    % Final result is a *sparse* tensor
    Ynt = sptenmat(Z, rdims, cdims, siz);
    Y = sptensor(Ynt);
else
    % Final result is a *dense* tensor
    Ynt = tenmat(Z, rdims, cdims, siz);
    Y = tensor(Ynt);
end

end


function h = image_histogram( im, mask, make_plot )
% IMAGE_HISTOGRAM  Calculate the histogram of the given image
%
% h = image_histogram( im, mask )
%
% calculate the histogram of the given image, optionally restricted on the mask
% h(1) is the number of pixels of value 0, etc.

% F. Nedelec, Dec. 2007
%
% Ramon Casero <rcasero@gmail.com>: Minor edits

%%compatibility with tiffread:
if ( isfield(im,'data') )
    im = double( im.data );
end

if nargin < 3
    make_plot = 0;
end

%%
if nargin < 2 || isempty(mask)

    max_val = ceil(max(reshape( im, numel(im), 1 )));

    h = histc( reshape( im, numel(im), 1 ), 0:max_val);
    %h = sum( histc( im, 0:max_val ), 2);

else

    if any( size(im) ~= size(mask) )
        error('Image and mask must be have the same size');
    end
    if min(reshape(mask, numel(mask), 1 )) < 0
        error('Values of the mask should be non-negative');
    end
    if max(reshape(mask, numel(mask), 1 )) > 1
        error('Values of the mask should be lower or equal to 1');
    end

    imm = im .* mask - ( 1-mask );
    val = reshape( imm, numel(imm), 1 );
    max_val = ceil(max(val));

    h = histc(val, 0:max_val);

end

%% Make a figure to display the histogram
if make_plot

    x = (0:size(h)-1)';
    figure('Name',inputname(1), 'Position', [100 150 800 300]);
    axes('Position', [0.05 0.1 0.9 0.8] );
    xlim([0 size(h,1)]);
    plot(x, h, 'g.' );
    title('Histogram of pixel values');

end

end


function Vout = disp_reaction(V,species)

% DISP_REACTION  Displays reactions for a given stoichiometric matrix.
%
% SYNTAX
%
% disp_reaction(V,species)
%
%   Displays the reactions corresponding given by the stoichiometric matrix
%   V. The columns of V correspond to reactions, the rows of V correspond
%   to species. species is a cell array of strings with labels for the
%   chemical species. Note that species is not parsed, so that labels can
%   need not be valid chemical formulas.
%
% Vout = disp_reaction(V,species)
% Vout = disp_reaction(V)
%
%   When called with an output argument, disp_reaction attempts to produce
%   integer coefficients for the stoichiometric matrix. The goal is to
%   express the coefficients efficiently. No reactions are displayed.
%

% AUTHOR
%
%    Jeff Kantor
%    December 19, 2010

    assert(nargin > 0, 'disp_reaction:input', ['No input. Expects a ', ...
                        'stoichiometric matrix.']);
    assert(nargin < 3, 'atomdisp_reactionic:input', 'Unexpected  inputs.');

    % Get size of the stoichiometric matrix. N is number of species, K is
    % number of reactions

    [N,K] = size(V);

    % Return if there are no species or no reactions to display.

    if ~isnumeric(V) || (N==0) || (K==0)
        disp('No reactions to display.');
        return
    end

    % Create generic species labels if no species are provided. Otherwise
    % check the list of species for obvious errors.

    if nargin < 2
        species = arrayfun(@(n)sprintf('Species_%d',n),1:N,'Uni',false);
    else
        assert(iscellstr(species), 'disp_reaction:input', ...
            'List of species must be a cell array.');
        assert(length(species(:)) == N, 'disp_reaction:input', ...
            'Number of species must equal rows of stoichiometry matrix.');
    end

    species = species(:);

    % The first step is to see if there is a better format for the
    % stoichiometric coefficients. We have 3 options for formatting the
    % stoichiometric coefficients: integer, rational, or floating point. We
    % form all three formats then choose a format for each reaction that
    % minimizes the display length.

    % Compute rational number approximation for the stoichiometric
    % coefficients.

    [num,den] = rat(V);

    % Find least common multiples of the denominators in each reaction. The
    % create output stoichiometric matrix with integer coefficients.

    Vout = zeros(N,K);
    for k = 1:K
        Vout(:,k) = num(:,k)*lcms(den(:,k))./den(:,k);
    end

    % Arrays to hold string representations of the stoichiometric
    % coefficients.

    Vs = cell(N,K);
    lens = zeros(1,K);

    Vf{N,K} = cell(N,K);
    lenf = zeros(1,K);

    Vr{N,K} = cell(N,K);
    lenr = zeros(1,K);

    % Smallest meaningful stoichiometric coefficient

    TOL = 1e-6;

    for k = 1:K
        for n = 1:N
            if abs(V(n,k)) >= TOL

                % Integer Coefficients

                Vs{n,k} = strtrim(sprintf('%d',abs(Vout(n,k))));

                if strcmp(Vs{n,k},'1')
                    Vs{n,k} = '';
                else
                    lens(k) = lens(k) + 1 + length(Vs{n,k});
                end

                % Rational Coefficients

                if (den(n,k) == 1) && (num(n,k) == 1);
                    Vr{n,k} = '';
                elseif (den(n,k) == 1)
                    Vr{n,k} = strtrim(sprintf('%d',abs(num(n,k))));
                    lenr(k) = lenr(k) + 1 + length(Vr{n,k});
                else
                    Vr{n,k} = strtrim(sprintf('%d/%d',abs(num(n,k)),den(n,k)));
                    lenr(k) = lenr(k) + 1 + length(Vr{n,k});
                end

                % Floating Point Coefficients

                Vf{n,k} = strtrim(sprintf('%10.5g',abs(V(n,k))));
                if strcmp(Vf{n,k},'1')
                    Vf{n,k} = '';
                else
                    lenf(k) = lenf(k) + 1 + length(Vf{n,k});
                end

            end
        end

        % Pick the best representation

        if (lenf(k) < lenr(k)) && (lenr(k) < lens(k))
            Vs(:,k) = Vf(:,k);
            lens(k) = lenf(k);
            Vout(:,k) = V(:,k);

        elseif (lenr(k) < lens(k))
            Vs(:,k) = Vr(:,k);
            lens(k) = lenr(k);
            Vout(:,k) = n(:,k)./den(:,k);

        end

    end

    % If there is no output then display the reactions

    if nargout < 1
        fprintf('\n');
        for k = 1:K

            pos = 1;

            % Print Reaction LHS -- Reactants

            n = find(V(:,k) < -TOL);
            pos = fprintrxn(strtrim(strcat(Vs(n,k),{' '},species(n))),pos);

            % Print separator

            if pos > 40
                fprintf('\n  <=> ');
                pos = 7;
            else
                fprintf(' <=> ');
                pos = pos + 5;
            end

            % Print Reaction RHS -- Products

            n = find(V(:,k) > TOL);
            fprintrxn(strtrim(strcat(Vs(n,k),{' '},species(n))),pos);

            fprintf('\n');

        end
        fprintf('\n');
    end

end

function q = lcms(v)

% LCMS  Find the least common multiple of a set of numbers.

    v = v(:);
    q = v(1);
    for k = 2:length(v)
        q = lcm(q,v(k));
    end
end

function pos = fprintrxn(terms,pos)

% FPRINTRXN  Helper function for displaying reactions.
%
% SYNTAX
%
% pos = fprintrxn(terms,pos)
%
%   Prints the reaction terms in the cell array terms separated by ' + '
%   starting at position pos. Breaks into multple lines if necessary.
%   Returns the position of the next character to be printed.

    % Print first term

    fprintf('%s',terms{1});
    pos = pos + length(terms{1});

    % Print remaining terms

    for m = 2:length(terms)

        % See where line would end

        tlen = 3 + length(terms{m});

        if (pos + tlen) <= 70

            fprintf(' + %s',terms{m});
            pos = pos + tlen;

        else % need a new line

            fprintf('\n   + %s',terms{m});
            pos = tlen + 2;

        end
    end
end


function tests = SO2Test
  tests = functiontests(localfunctions);
end

% we will assume that the primitives rotx,trotx, etc. all work

function constructor_test(tc)

    tc.verifyEqual(SO2().double, eye(2,2));

    %% from angle

    tc.verifyEqual(SO2(0).double, eye(2,2), 'AbsTol', 1e-10  );
    tc.verifyEqual(SO2(pi/2).double, rot2(pi/2), 'AbsTol', 1e-10  );
    tc.verifyEqual(SO2(90, 'deg').double, rot2(pi/2), 'AbsTol', 1e-10  );

    %% from R

    tc.verifyEqual(SO2( eye(2,2) ).double, eye(2,2), 'AbsTol', 1e-10  );

    tc.verifyEqual(SO2( rot2(pi/2) ).double, rot2(pi/2), 'AbsTol', 1e-10  );
    tc.verifyEqual(SO2( rot2(pi) ).double, rot2(pi), 'AbsTol', 1e-10  );

    %% from T
    tc.verifyEqual(SO2( trot2(pi/2) ).double, rot2(pi/2), 'AbsTol', 1e-10  );
    tc.verifyEqual(SO2( trot2(pi) ).double, rot2(pi), 'AbsTol', 1e-10  );

    %% R,T
    tc.verifyEqual(SO2( eye(2,2) ).R, eye(2,2), 'AbsTol', 1e-10  );

    tc.verifyEqual(SO2( rot2(pi/2) ).R, rot2(pi/2), 'AbsTol', 1e-10  );

    %% vectorised forms of R
    R = [];
    for theta = [-pi/2 0 pi/2 pi]
        R = cat(3, R, rot2(theta));
    end
    tc.verifyEqual(SO2(R).R, R, 'AbsTol', 1e-10);

    %% copy constructor
    r = SO2(rot2(0.3));
    tc.verifyEqual(SO2(r), r, 'AbsTol', 1e-10);

end

function concat_test(tc)
    x = SO2();
    xx = [x x x x];

    tc.verifyClass(xx, 'SO2');
    tc.verifySize(xx, [1 4]);
end

function primitive_convert_test(tc)
    % char

    s = char( SO2() );

end

function staticconstructors_test(tc)

    %% exponential
    tc.verifyEqual(SO2.exp( skew(0.3) ).R, rot2(0.3), 'AbsTol', 1e-10  );

end

function isa_test(tc)

    verifyTrue(tc, SO2.isa(rot2(0)) );
    verifyTrue(tc, SO2.isa(rot2(0), 'valid') );

    verifyFalse(tc, SO2.isa(1) )
end

function resulttype_test(tc)

    r = SO2();
    verifyClass(tc, r, 'SO2');

    verifyClass(tc, r*r, 'SO2');

    verifyClass(tc, r/r, 'SO2');

    verifyClass(tc, inv(r), 'SO2');

end

function multiply_test(tc)

    vx = [1 0]'; vy = [0 1]';
    r0 = SO2(0);
    r1 = SO2(pi/2);
    r2 = SO2(pi);
    u = SO2();

    %% SO2-SO2 product
    % scalar x scalar

    tc.verifyEqual(r0*u, r0);
    tc.verifyEqual(u*r0, r0);

    % vector x vector
    tc.verifyEqual([r0 r1 r2] * [r2 r0 r1], [r0*r2 r1*r0 r2*r1]);

    % scalar x vector
    tc.verifyEqual(r1 * [r0 r1 r2], [r1*r0 r1*r1 r1*r2]);

    % vector x scalar
    tc.verifyEqual([r0 r1 r2] * r2, [r0*r2 r1*r2 r2*r2]);

    %% SO2-vector product
    % scalar x scalar

    tc.verifyEqual(r1*vx, vy, 'AbsTol', 1e-10);

    % vector x vector
    tc.verifyEqual([r0 r1 r0] * [vy vx vx], [vy vy vx], 'AbsTol', 1e-10);

    % scalar x vector
    tc.verifyEqual(r1 * [vx vy -vx], [vy -vx -vy], 'AbsTol', 1e-10);

    % vector x scalar
    tc.verifyEqual([r0 r1 r2] * vy, [vy -vx -vy], 'AbsTol', 1e-10);
end

function divide_test(tc)

    r0 = SO2(0);
    r1 = SO2(pi/2);
    r2 = SO2(pi);
    u = SO2();

    % scalar / scalar
    % implicity tests inv

    tc.verifyEqual(r1/u, r1);
    tc.verifyEqual(r1/r1, u);

    % vector / vector
    tc.verifyEqual([r0 r1 r2] / [r2 r1 r0], [r0/r2 r1/r1 r2/r0]);

    % vector / scalar
    tc.verifyEqual([r0 r1 r2] / r1, [r0/r1 r1/r1 r2/r1]);
end

function conversions_test(tc)

    T = SO2(pi/2).SE2;
    verifyClass(tc, T, 'SE2');
    tc.verifyEqual(T.T, trot2(pi/2));

    %% Lie stuff
    th = 0.3;
    RR = SO2(th);
    tc.verifyEqual(RR.log, skew(th), 'AbsTol', 1e-10 );

end

function miscellany_test(tc)

    r = SO2( 0.3 );
    tc.verifyEqual(det(r), 1, 'AbsTol', 1e-10  );

    tc.verifyEqual(dim(r), 2);

    verifyEqual( tc, eig(r), eig(r.double) );

    verifyEqual( tc, isSE(r), false );

    verifyClass(tc, r.new, 'SO2');

    verifyClass(tc, SO2.convert(r), 'SO2');
    verifyClass(tc, SO2.convert( rot2(0.3) ), 'SO2');
    z = SO2.convert(r);
    tc.verifyEqual(double(z), double(r));

    z = SO2.convert(rot2(0.3));
    tc.verifyEqual(double(z), rot2(0.3));

    T = r.SE2;
    verifyClass(tc, SO2.convert(T), 'SO2');
end

function display_test(tc)

    R = SO2( 0.3 );

    R.print
    trprint2(R)   % old style syntax

    R.plot
    trplot2(R)   % old style syntax

    R2 = SO2(0.6);
    R.animate
    R.animate(R2)
    tranimate(R2)   % old style syntax
    tranimate(R, R2)   % old style syntax
    tranimate2(R2)   % old style syntax
    tranimate2(R, R2)   % old style syntax
end


function a = helmert_inverse ( n )

%*****************************************************************************80
%
%% HELMERT_INVERSE returns the inverse of the HELMERT matrix.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    20 October 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the order of A.
%
%    Output, real A(N,N), the inverse matrix.
%
  a = ( helmert ( n ) )';

  return
end


function [x, cost, info, options] = conjugategradient(problem, x, options)
% Conjugate gradient minimization algorithm for Manopt.
%
% function [x, cost, info, options] = conjugategradient(problem)
% function [x, cost, info, options] = conjugategradient(problem, x0)
% function [x, cost, info, options] = conjugategradient(problem, x0, options)
% function [x, cost, info, options] = conjugategradient(problem, [], options)
%
% Apply the conjugate gradient minimization algorithm to the problem
% defined in the problem structure, starting at x0 if it is provided
% (otherwise, at a random point on the manifold). To specify options whilst
% not specifying an initial guess, give x0 as [] (the empty matrix).
%
% In most of the examples bundled with the toolbox (see link below), the
% solver can be replaced by the present one if need be.
%
% The outputs x and cost are the best reached point on the manifold and its
% cost. The struct-array info contains information about the iterations:
%   iter : the iteration number (0 for the initial guess)
%   cost : cost value
%   time : elapsed time in seconds
%   gradnorm : Riemannian norm of the gradient
%   stepsize : norm of the last tangent vector retracted
%   beta : value of the beta parameter (see options.beta_type)
%   linesearch : information logged by options.linesearch
%   And possibly additional information logged by options.statsfun.
% For example, type [info.gradnorm] to obtain a vector of the successive
% gradient norms reached.
%
% The options structure is used to overwrite the default values. All
% options have a default value and are hence optional. To force an option
% value, pass an options structure with a field options.optionname, where
% optionname is one of the following and the default value is indicated
% between parentheses:
%
%   tolgradnorm (1e-6)
%       The algorithm terminates if the norm of the gradient drops below this.
%   maxiter (1000)
%       The algorithm terminates if maxiter iterations have been executed.
%   maxtime (Inf)
%       The algorithm terminates if maxtime seconds elapsed.
%   minstepsize (1e-10)
%       The algorithm terminates if the linesearch returns a displacement
%       vector (to be retracted) smaller in norm than this value.
%   beta_type ('H-S')
%       Conjugate gradient beta rule used to construct the new search
%       direction, based on a linear combination of the previous search
%       direction and the new (preconditioned) gradient. Possible values
%       for this parameter are:
%           'S-D', 'steep' for beta = 0 (preconditioned steepest descent)
%           'F-R' for Fletcher-Reeves's rule
%           'P-R' for Polak-Ribiere's modified rule
%           'H-S' for Hestenes-Stiefel's modified rule
%           'H-Z' for Hager-Zhang's modified rule
%       See Hager and Zhang 2006, "A survey of nonlinear conjugate gradient
%       methods" for a description of these rules in the Euclidean case and
%       for an explanation of how to adapt them to the preconditioned case.
%       The adaption to the Riemannian case is straightforward: see in code
%       for details. Modified rules take the max between 0 and the computed
%       beta value, which provides automatic restart, except for H-Z which
%       uses a different modification.
%   orth_value (Inf)
%       Following Powell's restart strategy (Math. prog. 1977), restart CG
%       (that is, make a -preconditioned- gradient step) if two successive
%       -preconditioned- gradients are "too" parallel. See for example
%       Hager and Zhang 2006, "A survey of nonlinear conjugate gradient
%       methods", page 12. An infinite value disables this strategy. See in
%       code formula for the specific criterion used.
%   linesearch (@linesearch_adaptive or @linesearch_hint)
%       Function handle to a line search function. The options structure is
%       passed to the line search too, so you can pass it parameters. See
%       each line search's documentation for info. Another available line
%       search in manopt is @linesearch, in /manopt/linesearch/linesearch.m
%       If the problem structure includes a line search hint, then the
%       default line search used in @linesearch_hint.
%   statsfun (none)
%       Function handle to a function that will be called after each
%       iteration to provide the opportunity to log additional statistics.
%       They will be returned in the info struct. See the generic Manopt
%       documentation about solvers for further information.
%   stopfun (none)
%       Function handle to a function that will be called at each iteration
%       to provide the opportunity to specify additional stopping criteria.
%       See the generic Manopt documentation about solvers for further
%       information.
%   verbosity (3)
%       Integer number used to tune the amount of output the algorithm
%       generates during execution (mostly as text in the command window).
%       The higher, the more output. 0 means silent.
%   storedepth (2)
%       Maximum number of different points x of the manifold for which a
%       store structure will be kept in memory in the storedb. If the
%       caching features of Manopt are not used, this is irrelevant. For
%       the CG algorithm, a store depth of 2 should always be sufficient.
%
%
% See also: steepestdescent trustregions manopt/solvers/linesearch manopt/examples

% This file is part of Manopt: www.manopt.org.
% Original author: Bamdev Mishra, Dec. 30, 2012.
% Contributors: Nicolas Boumal
% Change log:
%
%   March 14, 2013, NB:
%       Added preconditioner support : see Section 8 in
%       https://www.math.lsu.edu/~hozhang/papers/cgsurvey.pdf
%
%   Sept. 13, 2013, NB:
%       Now logging beta parameter too.
%
%	Nov. 7, 2013, NB:
%       The search direction is not normalized before it is passed to the
%       linesearch anymore. This way, it is up to the designers of the
%       linesearch to decide whether they want to use the norm of the
%       search direction in their algorithm or not. There are reasons
%       against it, but practical evidence that it may help too, so we
%       allow it. The default linesearch_adaptive used does exploit the
%       norm information. The base linesearch does not. You may select it
%       by setting options.linesearch = @linesearch;
%
%	Nov. 29, 2013, NB:
%       Documentation improved: options are now explicitly described.
%       Removed the Daniel rule for beta: it was not appropriate for
%       preconditioned CG and I could not find a proper reference for it.

% Verify that the problem description is sufficient for the solver.
if ~canGetCost(problem)
    warning('manopt:getCost', ...
        'No cost provided. The algorithm will likely abort.');
end
if ~canGetGradient(problem)
    warning('manopt:getGradient', ...
        'No gradient provided. The algorithm will likely abort.');
end

% Set local defaults here
localdefaults.minstepsize = 1e-10;
localdefaults.maxiter = 1000;
localdefaults.tolgradnorm = 1e-6;
localdefaults.storedepth = 2;
% Changed by NB : H-S has the "auto restart" property.
% See Hager-Zhang 2005/2006 survey about CG methods.
% Well, the auto restart comes from the 'max(0, ...)', not so much from the
% reason stated in Hager-Zhang I believe. P-R also has auto restart.
localdefaults.beta_type = 'H-S';
localdefaults.orth_value = Inf; % by BM as suggested in Nocedal and Wright

% Depending on whether the problem structure specifies a hint for
% line-search algorithms, choose a default line-search that works on
% its own (typical) or that uses the hint.
if ~canGetLinesearch(problem)
    localdefaults.linesearch = @linesearch_adaptive;
else
    localdefaults.linesearch = @linesearch_hint;
end

% Merge global and local defaults, then merge w/ user options, if any.
localdefaults = mergeOptions(getGlobalDefaults(), localdefaults);
if ~exist('options', 'var') || isempty(options)
    options = struct();
end
options = mergeOptions(localdefaults, options);

% for convenience
inner = problem.M.inner;
lincomb = problem.M.lincomb;

% Create a store database
storedb = struct();

timetic = tic();

% If no initial point x is given by the user, generate one at random.
if ~exist('x', 'var') || isempty(x)
    x = problem.M.rand();
end

% Compute objective-related quantities for x
[cost grad storedb] = getCostGrad(problem, x, storedb);
gradnorm = problem.M.norm(x, grad);
[Pgrad storedb] = getPrecon(problem, x, grad, storedb);
gradPgrad = inner(x, grad, Pgrad);

% Iteration counter (at any point, iter is the number of fully executed
% iterations so far)
iter = 0;

% Save stats in a struct array info and preallocate,
% see http://people.csail.mit.edu/jskelly/blog/?x=entry:entry091030-033941
stats = savestats();
info(1) = stats;
info(min(10000, options.maxiter+1)).iter = [];

% Initial linesearch memory
lsmem = [];

if options.verbosity >= 2
    fprintf(' iter\t                cost val\t     grad. norm\n');
end

% Compute a first descent direction (not normalized)
desc_dir = lincomb(x, -1, Pgrad);

% Start iterating until stopping criterion triggers
while true

    % Display iteration information
    if options.verbosity >= 2
        fprintf('%5d\t%+.16e\t%.8e\n', iter, cost, gradnorm);
    end

    % Start timing this iteration
    timetic = tic();

    % Run standard stopping criterion checks
    [stop reason] = stoppingcriterion(problem, x, options, info, iter+1);

    % Run specific stopping criterion check
    if ~stop && abs(stats.stepsize) < options.minstepsize
        stop = true;
        reason = 'Last stepsize smaller than minimum allowed. See options.minstepsize.';
    end

    if stop
        if options.verbosity >= 1
            fprintf([reason '\n']);
        end
        break;
    end

    % The line search algorithms require the directional derivative of the
    % cost at the current point x along the search direction.
    df0 = inner(x, grad, desc_dir);

    % If we didn't get a descent direction: restart, i.e., switch to the
    % negative gradient. Equivalent to resetting the CG direction to a
    % steepest descent step, which discards the past information.
    if df0 >= 0

        % Or we switch to the negative gradient direction.
        if options.verbosity >= 3
            fprintf(['Conjugate gradient info: got an ascent direction '...
                     '(df0 = %2e), reset to the (preconditioned) '...
                     'steepest descent direction.\n'], df0);
        end
        % Reset to negative gradient: this discards the CG memory.
        desc_dir = lincomb(x, -1, Pgrad);
        df0 = -gradPgrad;

    end

    % Execute line search
    [stepsize newx storedb lsmem lsstats] = options.linesearch(...
                 problem, x, desc_dir, cost, df0, options, storedb, lsmem);

    % Compute the new cost-related quantities for x
    [newcost newgrad storedb] = getCostGrad(problem, newx, storedb);
    newgradnorm = problem.M.norm(newx, newgrad);
    [Pnewgrad storedb] = getPrecon(problem, x, newgrad, storedb);
    newgradPnewgrad = inner(newx, newgrad, Pnewgrad);

    % Apply the CG scheme to compute the next search direction.
    %
    % This paper https://www.math.lsu.edu/~hozhang/papers/cgsurvey.pdf
	% by Hager and Zhang lists many known beta rules. The rules defined
    % here can be found in that paper (or are provided with additional
    % references), adapted to the Riemannian setting.
	%
    if strcmpi(options.beta_type, 'steep') || ...
       strcmpi(options.beta_type, 'S-D')              % Gradient Descent

        beta = 0;
        desc_dir = lincomb(x, -1, Pnewgrad);

    else

        oldgrad = problem.M.transp(x, newx, grad);
        orth_grads = inner(newx, oldgrad, Pnewgrad)/newgradPnewgrad;

        % Powell's restart strategy (see page 12 of Hager and Zhang's
        % survey on conjugate gradient methods, for example)
        if abs(orth_grads) >= options.orth_value,
            beta = 0;
            desc_dir = lincomb(x, -1, Pnewgrad);

        else % Compute the CG modification

            desc_dir = problem.M.transp(x, newx, desc_dir);

            if strcmp(options.beta_type, 'F-R')  % Fletcher-Reeves
                beta = newgradPnewgrad / gradPgrad;

            elseif strcmp(options.beta_type, 'P-R')  % Polak-Ribiere+
                % vector grad(new) - transported grad(current)
                diff = lincomb(newx, 1, newgrad, -1, oldgrad);
                ip_diff = inner(newx, Pnewgrad, diff);
                beta = ip_diff/gradPgrad;
                beta = max(0, beta);

            elseif strcmp(options.beta_type, 'H-S')  % Hestenes-Stiefel+
                diff = lincomb(newx, 1, newgrad, -1, oldgrad);
                ip_diff = inner(newx, Pnewgrad, diff);
                beta = ip_diff / inner(newx, diff, desc_dir);
                beta = max(0, beta);

            elseif strcmp(options.beta_type, 'H-Z') % Hager-Zhang+
                diff = lincomb(newx, 1, newgrad, -1, oldgrad);
                Poldgrad = problem.M.transp(x, newx, Pgrad);
                Pdiff = lincomb(newx, 1, Pnewgrad, -1, Poldgrad);
                deno = inner(newx, diff, desc_dir);
                numo = inner(newx, diff, Pnewgrad);
                numo = numo - 2*inner(newx, diff, Pdiff)*...
                                       inner(newx, desc_dir, newgrad)/deno;
                beta = numo/deno;

                % Robustness (see Hager-Zhang paper mentioned above)
                desc_dir_norm = problem.M.norm(newx, desc_dir);
                eta_HZ = -1/(desc_dir_norm * min(0.01, gradnorm));
                beta = max(beta,  eta_HZ);

            else
                error(['Unknown options.beta_type. ' ...
                       'Should be steep, S-D, F-R, P-R, H-S or H-Z.']);
            end
            desc_dir = lincomb(newx, -1, Pnewgrad, beta, desc_dir);
        end

    end

    % Make sure we don't use too much memory for the store database.
    storedb = purgeStoredb(storedb, options.storedepth);

    % Update iterate info
    x = newx;
    cost = newcost;
    grad = newgrad;
    Pgrad = Pnewgrad;
    gradnorm = newgradnorm;
    gradPgrad = newgradPnewgrad;

    % iter is the number of iterations we have accomplished.
    iter = iter + 1;

    % Log statistics for freshly executed iteration
    stats = savestats();
    info(iter+1) = stats; %#ok<AGROW>

end

info = info(1:iter+1);

if options.verbosity >= 1
    fprintf('Total time is %f [s] (excludes statsfun)\n', info(end).time);
end

% Routine in charge of collecting the current iteration stats
    function stats = savestats()
        stats.iter = iter;
        stats.cost = cost;
        stats.gradnorm = gradnorm;
        if iter == 0
            stats.stepsize = nan;
            stats.time = toc(timetic);
            stats.linesearch = [];
            stats.beta = 0;
        else
            stats.stepsize = stepsize;
            stats.time = info(iter).time + toc(timetic);
            stats.linesearch = lsstats;
            stats.beta = beta;
        end
        stats = applyStatsfun(problem, x, storedb, options, stats);
    end

end


function[x] = IMSVD(Y,U)
% inverse MSVD (IMSVD)
% Inputs-> Y: MSVD coefficients & U: unitary matrix (U in SVD)
% output-> x: image (spaitial domain)

[m,n] = size(Y.LL);
mn = m*n;
T = zeros(4,mn);
T(1,:) = reshape(Y.LL,1,mn);
T(2,:) = reshape(Y.LH,1,mn);
T(3,:) = reshape(Y.HL,1,mn);
T(4,:) = reshape(Y.HH,1,mn);
A = U * T;
x = zeros(m*2,n*2);
for j = 1:n
    for i = 1:m
        x((i-1)*2+(1:2), (j-1)*2+(1:2)) = reshape(A(:,i+(j-1)*m),2,2);
    end
end


function geoEastOfNorth=magHeading2Geog(points,PEastOfNorth,coeffType,year,a,f)
%MAGHEADING2GEOG Convert a heading in terms of radians clockwise
%                (East) of MAGNETIC North to a heading in terms of radians
%                clockwise from GEOGRAPHIC North as defined on a particular
%                reference ellipsoid. The model for the Earth's magnetic
%                field can be selected. In the rare spots (i.e. near the
%                magnetic poles), where the magnetic field vector points
%                directly towards the reference ellipsoid, the geographic
%                heading will be undefined and a NaN will be returned.
%
%INPUTS: points One or more points given in geodetic latitude and
%               longitude, in radians, and height, in meters where the
%               magnetic headings apply. To convert N headings, points is a
%               3XN matrix with each column having the format
%               [latitude;longitude; height]. All points should be
%               associated with the same time. At the geographic poles, the
%               longitude value determines the orientation of the local
%               coordinate system axes. Thus, geographic headings ARE
%               defined at the poles.
%  PEastOfNorth An NX1 array of N magnetic headings in radians clockwise
%               from North that should be turned into geographic headings.
%     coeffType This specifies the coefficient model for the coefficients.
%               If one wishes to explcitely pass a model, then this is a
%               structure with members C, S, aH, and cH, which are defined
%               in the same manner as the return values from the function
%               getWMMCoeffs. Otherwise, a string can be passed. Possible
%               values are:
%               'WMM' (The default if omitted or an empty matrix is
%                     passed). Use the World Magnetic Model via the
%                     function getWMMCoeffs.
%               'IGRF' Use the International Geomagnetic Reference Field
%                     model via the function getIGRFCoeffs.
%          year This is only used if coeffType is a string. A decimal
%               number indicating a year in the Gregorian calendar as
%               specified by UTC. For example, halfway through the year
%               2012 would be represented as 2012.5. The precision of the
%               model is not sufficiently fine that leap seconds matter. If
%               this parameter is omitted, then the last reference epoch of
%               the geomagnetic model is used.
%             a The semi-major axis of the reference ellipsoid. If this
%               argument is omitted, the value in
%               Constants.WGS84SemiMajorAxis is used.
%             f The flattening factor of the reference ellipsoid. If this
%               argument is omitted, the value in
%               Constants.WGS84Flattening is used.
%
%OUTPUT: geoEastOfNorth The headings converted to radians clockwise of
%                       geographic North on the reference ellipse.
%
%First, spherical harmonic coefficients for the geomagnetic model are
%obtained at the desired time and points. Then, the magnetic flex vector B
%is determined at each of the points. Strictly speaking, the direction of
%magnetic North would be the component of the magnetic flux vector
%projected onto the local gravitationally horizontal plane at each point.
%However, since B is already rather imprecise, nothing is really lost by
%using a projection onto the local tangent plane of the reference
%ellipsoid of the Earth. Thus, rotating B about the negative of the local
%vertical to the reference ellipsoid by PEastOfNorth provides a vector
%whose projection into the local tangent plane points in the desired
%geographic heading.
%
%This function makes use of the functions getWMM2010Coeffs and
%spherHarmonicEval to determine the magnetic flux vector at the specified
%points as well as the function getENUAxes to determine the local
%East-North-Up coordinate axes.
%
%January 2014 David F. Crouse, Naval Research Laboratory, Washington D.C.
%(UNCLASSIFIED) DISTRIBUTION STATEMENT A. Approved for public release.

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

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

if(nargin<3||isempty(coeffType))
    coeffType='WMM';
end

if(nargin<4)
    year=[];
end

%Get the fully normalized spherical harmonic coefficients for the
%geomagnetic model.
if(isstruct(coeffType))
    %If the user explicitly provided the coefficients.
    C=coeffType.C;
    S=coeffType.S;
    aH=coeffType.aH;
    cH=coeffType.cH;
else
    switch(coeffType)
        case 'WMM'
            [C,S,aH,cH]=getWMMCoeffs(year);
        case 'IGRF'
            [C,S,aH,cH]=getIGRFCoeffs(year);
        otherwise
            error('Unknown coefficients selected')
    end
end

[~,gradV]=spherHarmonicEval(C,S,ellips2Sphere(points,a,f),aH,cH,true);
B=-gradV;
%B is now a matrix of vectors of the magnetic flux of the Earth's field at
%the points. The direction of B in the local tangent plane to the reference
%ellipsoid approximately defined magnetic north.

numPoints=size(points,2);
geoEastOfNorth=zeros(numPoints,1);%Allocate space
for curPoint=1:numPoints
    %-B(:,curPoint) defines the direction of magnetic North at the current
    %point. We want the direction vector PEastOfNorth(curPoint). To go East
    %of North, one must rotate that number of degrees about the local
    %"Down" vector.
    u=getENUAxes(points(:,curPoint),false,a,f);

    %The negative of u(:,3) is a unit vector in the down direction.
    vRot=rotateVectorAxisAng(B(:,curPoint),-u(:,3),PEastOfNorth(curPoint));

    %Given the rotated field vector, only the components in the local
    %tangent plane matter.
    vEast=dot(vRot,u(:,1));
    vNorth=dot(vRot,u(:,2));
    %Find the angle East of North.
    geoEastOfNorth(curPoint)=atan2(vEast,vNorth);
end

end

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


function g = compute_gratio(D)

% Ikeda M, Oka Y. Brain Behav, 2012. "The relationship between nerve conduction velocity and fiber morphology during peripheral nerve regeneration."
g = 0.220 .* log10(2*D) + 0.508;
% g = 0.76; % if you want a constant g-ratio

% figure
% plot(2.*R, g)

end


function coded_bits = convolution_encoder(in_bits)
ConvCodeGenPoly=[1 0 1 1 0 1 1;1 1 1 1 0 0 1];
Nrow = size(ConvCodeGenPoly,1);
Nbits=size(ConvCodeGenPoly,2)+length(in_bits)-1;
uncoded_bits=zeros(Nrow,Nbits);
for row=1:Nrow
    uncoded_bits(row,1:Nbits)=rem(conv(in_bits,ConvCodeGenPoly(row,:)),2);
end
coded_bits=uncoded_bits;
end


%BAGGINGC Bootstrapping and aggregation of classifiers
%
%    W = BAGGINGC (A,CLASSF,N,ACLASSF,T)
%
% INPUT
%   A         Training dataset.
%   CLASSF    The base classifier (default: nmc)
%   N         Number of base classifiers to train (default: 100)
%   ACLASSF   Aggregating classifier (default: meanc), [] for no aggregation.
%   T         Tuning set on which ACLASSF is trained (default: [], meaning use A)
%
% OUTPUT
%    W        A combined classifier (if ACLASSF given) or a stacked
%             classifier (if ACLASSF []).
%
% DESCRIPTION
% Computation of a stabilised version of a classifier by bootstrapping and
% aggregation ('bagging'). In total N bootstrap versions of the dataset A
% are generated and used for training of the untrained classifier CLASSF.
% Aggregation is done using the combining classifier specified in CCLASSF.
% If ACLASSF is a trainable classifier it is trained by the tuning dataset
% T, if given; else A is used for training. The default aggregating classifier
% ACLASSF is MEANC. Default base classifier CLASSF is NMC.
%
% SEE ALSO (PRTools Guide)
% DATASETS, MAPPINGS, NMC, MEANC

% Copyright: R.P.W. Duin, duin@ph.tn.tudelft.nl
% Faculty of Applied Sciences, Delft University of Technology
% P.O. Box 5046, 2600 GA Delft, The Netherlands

% $Id: baggingc.m,v 1.3 2010/06/01 08:47:05 duin Exp $

function w = baggingc (a,clasf,n,rule,t)
		if (nargin < 5),
		prwarning(2,'no tuning set supplied, using training set for tuning (risk of overfit)');
		t = [];
	end
	if (nargin < 4)
		prwarning(2,'aggregating classifier not specified, assuming meanc');
		rule = meanc;
	end
	if (nargin < 3) | isempty(n),
		prwarning(2,'number of repetitions not specified, assuming 100');
		n = 100;
	end
	if (nargin < 2) | isempty(clasf),
		prwarning(2,'base classifier not specified, assuming nmc');
		clasf = nmc;
	end
	if ((nargin < 1) | isempty(a))
		w = prmapping('baggingc',{clasf,n,rule});
		return
	end

	iscomdset(a,t); % test compatibility training and tuning set

	% Concatenate N classifiers on bootstrap samples (100%) taken
	% from the training set.

	w = [];
	for i = 1:n
		w = [w gendat(a)*clasf];
	end

	% If no aggregating classifier is given, just return the N classifiers...

	if (~isempty(rule))

		% ... otherwise, train the aggregating classifier on the train or
		% tuning set.

		if (isempty(t))
			w = traincc(a,w,rule);
		else
			w = traincc(t,w,rule);
		end
	end

	w = setcost(w,a);

	return


function [err_rel,err_abs] = derivcheck(f,x,flag)
%DERIVCHECK Check analytical vs numerical differentiation for a function

if nargin < 3 || isempty(flag); flag = false; end

tic
if flag
    dy_num = fgrad(f,x,'five-points');
else
    dy_num = gradest(f,x);
end
toc
tic
[y,dy_ana] = f(x);
toc

if size(dy_num,1) == size(dy_num,2)
    dy_num = sum(dy_num,1);
end

% Reshape to row vectors
dy_num = dy_num(:)';
dy_ana = dy_ana(:)';

fprintf('Relative errors:\n');
err_rel = (dy_num(:)' - dy_ana(:)')./dy_num(:)'

fprintf('Absolute errors:\n');
err_abs = dy_num(:)' - dy_ana(:)'

end


function b_n = beamWeightsTorus2Spherical(N)
%BEAMWEIGHTSTORUS2SPHERICAL Generate beamweights for a raised torus
%
%   The tabulated beamweights below describe toroidal patterns of the form
%   D(theta) = |sin(\theta)|^N, using up to 4th-order components.
%   Note that N=2 and N=4 are exactly band-limited to 2nd and 4th-order
%   respectively, while the N=1 and N=3 extend to higher-orders with
%   decaying energy, and hence the weights below are just an approximation.
%   Because the pattern is axisymmetric only the N+1 coefficients of m=0
%   are returned.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% BEAMWEIGHTSTORUS2SPHERICAL.M - 11/7/2013
% Archontis Politis, archontis.politis@aalto.fi
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

switch N
    case 1
        b_n = [2.7842    0.0000   -0.7781    0.0000   -0.1303];
    case 2
        b_n = [2.3633   -0.0000   -1.0569];
    case 3
        b_n = [2.0881   -0.0000   -1.1673    0.0000    0.1468];
    case 4
        b_n = [1.8906   -0.0000   -1.2079    0.0000    0.2701];
end


function [A,sumV] = quadcof(N,NW,order)
% Helper function to calculate the nonstationary quadratic inverse matrix
% Usage: [A,sumV] = quadcof(N,NW,order)
% N             (number of samples)
% NW: Time bandwidth product
% order: order (number of coefficients, upto 4NW)
%
% Outputs:
%
% A: quadratic inverse coefficient matrix
% sumV: sum of the quadratic inverse eigenvectors

A = zeros(2*NW,2*NW,order);
V = quadinv(N,NW);
[P,alpha] = dpss(N,NW,'calc');

for ii = 1:order

  for jj = 1:2*NW
    for kk = 1:2*NW
	A(jj,kk,ii) = sqrt(alpha(jj)*alpha(kk))*...
                      sum(P(:,jj).*P(:,kk).*V(:,ii));
    end;
  end;
end;
sumV=sum(V)/N;


function mv = mv_pca(mv,centered);
%
% mv = mv_pca([mv],[centered]);
%
% Perform PCA on multi-voxel data.
%
%
%
% ras 05/05.
if ieNotDefined('mv')
    mv = get(gcf,'UserData');
end

if ieNotDefined('centered')
    % this is currently not used,
    % since I always do it both ways
    centered = 1;
end

% get the voxels x conditions amplitudes matrix
amps = mv_amps(mv);

% apply PCA
[pcs score k Uk Lk] = pca_all(amps); % kalanit's code, uncentered
eigenvals = Lk(sub2ind(size(Lk),1:k,1:k));      % diagonal entries of gamma mat
varExplained = 100 .* eigenvals ./ sum(eigenvals);     % percent variance explained

% redo centered, using MATLAB command
[pcs score latent tsquare] = princomp(amps);

% add to mv struct
mv.pca.pcs = pcs;
mv.pca.score = score;
mv.pca.latent = latent;
mv.pca.tsquare = tsquare;
mv.pca.k = k;
mv.pca.Uk = Uk;
mv.pca.Lk = Lk;

% if a UI exists, visualize, set as user data
if isfield(mv.ui,'fig') & ishandle(mv.ui.fig)
    set(mv.ui.fig,'UserData',mv);
    figure(mv.ui.fig)
    multiVoxelUI; % refresh UI
end

return


%rate_a=ach_simo_nocsi(nn,P,error,rx,K) computes the achievability bound on
%the maximal channel coding rate over a single-input multiple-output (SIMO)
%quasi-static Rician fading channel with no channel state information at
%transmitter or receiver.
%
%The function takes the following inputs:
%
%nn: blocklength, scalar/vector;
%P: input power (in linear scale), scalar;
%error: target block error probability, scalar, should be greater than 10^(-6);
%rx: number of receive antennas, scalar;
%K: rician K-factor, scalar; the default value is 0.
function rate_a=ach_simo_nocsi(nn,P,error,rx,K)

if (nargin < 5) || isempty(K)
	K = 0;
end

loop=min(1000/error, 10^7);

rate_a=[];
hh = sqrt(0.5/(K+1))*(randn(rx,loop)+1i*randn(rx,loop)) + sqrt(K/(K+1)); %samples of Rician fading channels
w1 = sqrt(0.5) * (randn(rx, loop) + 1i*randn(rx,loop)); %AWGN noise

for n=nn
    data_a=zeros(1,loop);

    %By spherical symmetry, it suffices to consider x0=[(nP)^{-1/2},0,...,0]
    y1 = hh.*sqrt(n*P) + w1; %The received vector during the first channel use
    for ii=1:loop
        y1_sample = y1(:,ii);
        W = sqrt(0.5) * (randn(rx, n-1) + 1i*randn(rx,n-1));

        %projection of x0 onto Y
        data_a(ii) = abs(y1_sample'* inv(y1_sample*y1_sample' + W*W')*y1_sample);
        %
    end

    data_sort = sort(data_a);

    %maximize over tau...
    tau_array = [floor(error*loop)-1:-1:1]/loop ;

    gamma_tau = 1 - data_sort(1:floor(error*loop)-1);

    log_Fn = log(betainc(gamma_tau, n-rx,rx));
    index_set= find (log_Fn < -10^100);%detect if the output of betainc is zero

    if length(index_set)>0
       log_Fn(index_set) = (n-rx) * log(gamma_tau(index_set))  - sum(log(n-rx+1:1:n-1)) + log(gamma(rx)) ;
    end

    %
    log_M = max(log(tau_array) - log_Fn );

    rate_a =[rate_a, log_M/n/log(2)];
end


%Test script for the ARGUS method

clear; %close all;
%path added for regression methods
%Set seed for repeatability
%s = RandStream('mt19937ar','Seed',1);
%RandStream.setGlobalStream(s);
rand('seed',1)

%Set initial conditions
intialSize =5;
dim = 2;
%Set stop conditions
maxPoints = 1000;
pntsAdd = 10;

%Set function handle for loop (not needed for ARGUS)
stdr = .5;
%fun = @(x)sin(6*x)+stdr*randn(length(x),1);
fun = @(x)sin(6*sum(x,2))+stdr*randn(length(x(:,1)),1);
range = 0:.01:1;
[x1 x2] = meshgrid(range);
y = sin(6*(x1(:)+x2(:)));
subplot(2,2,2)
hold off;
surf(x1,x2,reshape(y,size(x1)),'EdgeColor',[.5 .5 .5],'FaceAlpha',0);
hold on;
subplot(2,2,4)
hold off;
surf(x1,x2,stdr*ones(size(x2)),'EdgeColor',[.5 .5 .5],'FaceAlpha',0);
hold on;
%plot(0:.01:1,sin(6*(0:.01:1)'))

%Initial conditions
x = lhsdesign(intialSize,dim);
y= feval(fun,x);
XYdata = add2XYdata([],x,y);
subplot(2,2,4)
scatter3(x(:,1),x(:,2),zeros(size(x(:,1))),'rs')
subplot(2,2,2)
scatter3(x(:,1),x(:,2),y,'rs')
subplot(2,2,1)
hold off
scatter(x(:,1),x(:,2),'rs')
hold on
%plot(x,y,'rs')

%Work horse
i=intialSize;
[newpoints hist] = ARGUS(XYdata,pntsAdd);
ynew = feval(fun,newpoints);
XYdata = add2XYdata(XYdata,newpoints,ynew);
%plot(newpoints,ynew,'ks')
%text(newpoints,ynew,['\leftarrow' sprintf('%i',i)]);
brk =floor(pntsAdd*hist.ratio(end));
subplot(2,2,1)
scatter(newpoints(1:brk,1),newpoints(1:brk,2),'ko')
scatter(newpoints(brk+1:end,1),newpoints(brk+1:end,2),'bx')
%legend('Initial Deisgn','Replication','Exploration','Location','Best')
%legend('boxoff')
h_oldstErr=surf(x1,x2,reshape(feval(hist.stErrfun,[x1(:),x2(:)]),size(x1)),'Edgecolor','none','FaceAlpha',.2);
colorbar
xlabel('X1')
ylabel('X2')
title('Standard Error')
subplot(2,2,3)
plot(1:hist.itt,floor(pntsAdd*hist.ratio)/pntsAdd,'bs-')
xlabel('Iteration')
ylabel('Replications/Exploration')
subplot(2,2,2)
title('Mean')
scatter3(newpoints(1:brk,1),newpoints(1:brk,2),ynew(1:brk),'ko')
scatter3(newpoints(brk+1:end,1),newpoints(brk+1:end,2),ynew(brk+1:end),'bx')
h_oldmu = surf(x1,x2,reshape(feval(hist.mufun,[x1(:),x2(:)]),size(x1)),'Edgecolor','none','FaceAlpha',.5);
%text(newpoints(:,1),newpoints(:,2),ynew,['\leftarrow' sprintf('%i',i)]);
subplot(2,2,4)
title('Standard Deviation')
scatter3(newpoints(1:brk,1),newpoints(1:brk,2),zeros(size(newpoints(1:brk,1))),'ko')
scatter3(newpoints(brk+1:end,1),newpoints(brk+1:end,2),zeros(size(newpoints(brk+1:end,1))),'bx')
h_oldsig = surf(x1,x2,reshape(feval(hist.stdfun,[x1(:),x2(:)]),size(x1)),'Edgecolor','none','FaceAlpha',.5);
%text(newpoints(:,1),newpoints(:,2),zeros(size(newpoints(:,1))),['\leftarrow' sprintf('%i',i)]);
i=intialSize+pntsAdd;
while maxPoints>i
    [newpoints hist] = ARGUS(XYdata,pntsAdd,hist);
    ynew = feval(fun,newpoints);
    XYdata = add2XYdata(XYdata,newpoints,ynew);
    %plot(newpoints,ynew,'ks')
    brk =floor(pntsAdd*hist.ratio(end));
    subplot(2,2,1)
    scatter(newpoints(1:brk,1),newpoints(1:brk,2),'ko')
    scatter(newpoints(brk+1:end,1),newpoints(brk+1:end,2),'bx')
    h_newstErr=surf(x1,x2,reshape(feval(hist.stErrfun,[x1(:),x2(:)]),size(x1)),'Edgecolor','none','FaceAlpha',.2);
    %colorbar
    delete(h_oldstErr);
    h_oldstErr=h_newstErr;
    subplot(2,2,3)
    plot(1:hist.itt,floor(pntsAdd*hist.ratio)/pntsAdd,'bs-')
    xlabel('Iteration')
    ylabel('Replications/Exploration')
    subplot(2,2,2)
    scatter3(newpoints(1:brk,1),newpoints(1:brk,2),ynew(1:brk),'ko')
    scatter3(newpoints(brk+1:end,1),newpoints(brk+1:end,2),ynew(brk+1:end),'bx')
    h_newmu = surf(x1,x2,reshape(feval(hist.mufun,[x1(:),x2(:)]),size(x1)),'Edgecolor','none','FaceAlpha',.5);
    %text(newpoints(:,1),newpoints(:,2),ynew,['\leftarrow' sprintf('%i',i)]);
    delete(h_oldmu);
    h_oldmu=h_newmu;
    subplot(2,2,4)
    scatter3(newpoints(1:brk,1),newpoints(1:brk,2),zeros(size(newpoints(1:brk,1))),'ko')
    scatter3(newpoints(brk+1:end,1),newpoints(brk+1:end,2),zeros(size(newpoints(brk+1:end,1))),'bx')
    h_newsig = surf(x1,x2,reshape(feval(hist.stdfun,[x1(:),x2(:)]),size(x1)),'Edgecolor','none','FaceAlpha',.5);
    %text(newpoints(:,1),newpoints(:,2),zeros(size(newpoints(:,1))),['\leftarrow' sprintf('%i',i)]);
    delete(h_oldsig);
    h_oldsig=h_newsig;
    %text(newpoints,ynew,['\leftarrow' sprintf('%i',i)]);
    i=i+pntsAdd;
    if mod(i-intialSize,3*pntsAdd)==0
        fprintf('Done with %i\n',i);end
end


%  Updates the log of the scaling parameter for mcmc algorithms
%
%  ::
%
%    log_c=update_scaling(log_c,accept_ratio,alpha_range,fixed_scaling,n,xi3)
%    log_c=update_scaling(log_c,accept_ratio,alpha_range,fixed_scaling,n,xi3,c3)
%    log_c=update_scaling(log_c,accept_ratio,alpha_range,fixed_scaling,n,xi3,c3,c_range)
%
%  Args:
%
%     - **log_c** [numeric]: initial scaling
%     - **accept_ratio** [numeric]: acceptance rate
%     - **alpha_range** [interval|{[]}]: target acceptance range
%     - **fixed_scaling** [true|false]: Do not update the scale of the algorithm
%     - **n** [integer]: iteration
%     - **xi3** [numeric]: appears in gam3 = c3*(n+1)^(-xi3). must lie in
%       (0.5,1)
%     - **c3** [numeric|{1}]: appears in gam3 = c3*(n+1)^(-xi3);
%     - **c_range** [interval|{[sqrt(eps),100]}]: range of variation of the
%       scaling parameter (not its log!!!)
%
%  Returns:
%     :
%
%     - **log_c** [numeric]: updated scaling
%
%  Note:
%     Adapts formulae 20 in Blazej Miasojedow, Eric Moulines and Matti
%     Vihola (2012): "Adaptive Parallel Tempering Algorithm"
%
%


function [W] = Btuph2W(Btuph)
% Convert power from British thermal units per hour to watts.
% Chad A. Greene 2012
W = Btuph*0.29307107;


function [I,J,K]=MRcart2imNeg(Xc,Yc,Zc,v,OR,rn,cn)

%--------------------------------------------------------------------------
% function [I,J,K]=MRcart2im(X,Y,Z,v,OR,r,c)
%
% This function calculates the image coordinates I,J,K of the cartesian
% coordinates X,Y,Z based on the voxel dimensions v, the origin OR (cartesian
% coorindates of the point I=1, J=1, K=1), the offsets r anc c.
%
% v=N_info.PixelSpacing;
% v(3)=N_info.SliceThickness;
% OR=N_info.ImagePositionPatient;
% r=N_info.ImageOrientationPatient(4:6);
% c=N_info.ImageOrientationPatient(1:3);
%
% Based on:
% http://cmic.cs.ucl.ac.uk/fileadmin/cmic/Documents/DavidAtkinson/DICOM_6up.pdf
% and the file "TranTransform matrix between two dicom image coordinates"
% from the matlab central file exchange written by Alper Yaman.
%--------------------------------------------------------------------------

reForm=~isvector(Xc);

%Switch convention
X=Yc; Y=Xc; Z=Zc;

size_M=size(Xc);

% r => row direction vector
r=vecnormalize(cn);

% c => column direction vector
c=vecnormalize(rn);

%Determine s => slice direction vector N.B. defined as cross(c,r) not
%cross(r,c)
s=cross(c',r'); %Determine s => slice direction vector
s=vecnormalize(s);

X=X(:)';Y=Y(:)';Z=Z(:)';

%Translation
T  = [1 0 0 OR(1);...
      0 1 0 OR(2);...
      0 0 1 OR(3);...
      0 0 0 1];
%Rotation
R  = [r(1) c(1) s(1) 0;...
      r(2) c(2) s(2) 0;...
      r(3) c(3) s(3) 0;...
      0    0    0    1];
%Scaling
S  = [v(1) 0    0    0;...
      0    v(2) 0    0;...
      0    0    v(3) 0;...
      0    0    0    1];

T0 = eye(size(T));

M  = T * R * S * T0; %The transformation matrix

XYZ=ones(4,length(X));
XYZ(1,:)=X;
XYZ(2,:)=Y;
XYZ(3,:)=Z;

IJK=(M\XYZ)';

I=IJK(:,1)+1; J=IJK(:,2)+1; K=IJK(:,3)+1;

if reForm %If the input is not a vector reshape
    I=reshape(I,size_M);
    J=reshape(J,size_M);
    K=reshape(K,size_M);
end

end

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% 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.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function aic = ml_gmm_aic(Data, Priors,Mu,Sigma,cov_type)
%ML_GMM_AIC Akaike Information criterion
%
%  input ------------------------------------------------------------------
%
%   o Data:     D x N array representing N datapoints of D dimensions.
%
%   o Priors:   1 x K array representing the prior probabilities of the
%               K GMM components.
%   o Mu:       D x K array representing the centers of the K GMM components.
%
%   o Sigma:    D x D x K array representing the covariance matrices of the
%               K GMM components.
%
%   o cov_type: string, covariance type = 'full','diag' or 'iso'
%
%  output -----------------------------------------------------------------
%
%   o aic   : (1 x 1)
%
%
[D,~]       = size(Data);
K           = length(Priors);
num_param   = K-1 + K * D;

if strcmp(cov_type,'full') == true
    num_param = num_param + K * ( D * ( D - 1)/2 );
elseif strcmp(cov_type,'diag') == true
    num_param = num_param + K * D;
elseif strcmp(cov_type,'iso') == true
    num_param = num_param + K * 1;
else
   error(['no such covariance type: ' cov_type '  only full | diag | isot ']);
end

% compute the loglikelihood of the data given the model
% loglik: (N x 1)
loglik = ml_LogLikelihood_gmm(Data,Priors,Mu,Sigma);

aic = - 2 * loglik + 2 * num_param;

end