function [mssim, ssim_map, mcs, cs_map] = ssim_index_new(img1, img2, K, window)

if (nargin < 2 | nargin > 4)
   ssim_index = -Inf;
   ssim_map = -Inf;
   return;
end

if (size(img1) ~= size(img2))
   ssim_index = -Inf;
   ssim_map = -Inf;
   return;
end

[M N] = size(img1);

if (nargin == 2)
   if ((M < 11) | (N < 11))
	   ssim_index = -Inf;
	   ssim_map = -Inf;
      return
   end
   window = fspecial('gaussian', 11, 1.5);	%
   K(1) = 0.01;										% default settings
   K(2) = 0.03;										%
end

if (nargin == 3)
   if ((M < 11) | (N < 11))
	   ssim_index = -Inf;
	   ssim_map = -Inf;
      return
   end
   window = fspecial('gaussian', 11, 1.5);
   if (length(K) == 2)
      if (K(1) < 0 | K(2) < 0)
		   ssim_index = -Inf;
   		ssim_map = -Inf;
	   	return;
      end
   else
	   ssim_index = -Inf;
   	ssim_map = -Inf;
	   return;
   end
end

if (nargin == 4)
   [H W] = size(window);
   if ((H*W) < 4 | (H > M) | (W > N))
	   ssim_index = -Inf;
	   ssim_map = -Inf;
      return
   end
   if (length(K) == 2)
      if (K(1) < 0 | K(2) < 0)
		   ssim_index = -Inf;
   		ssim_map = -Inf;
	   	return;
      end
   else
	   ssim_index = -Inf;
   	ssim_map = -Inf;
	   return;
   end
end

C1 = (K(1)*255)^2;
C2 = (K(2)*255)^2;
window = window/sum(sum(window));

mu1   = filter2(window, img1, 'valid');
mu2   = filter2(window, img2, 'valid');
mu1_sq = mu1.*mu1;
mu2_sq = mu2.*mu2;
mu1_mu2 = mu1.*mu2;
sigma1_sq = filter2(window, img1.*img1, 'valid') - mu1_sq;
sigma2_sq = filter2(window, img2.*img2, 'valid') - mu2_sq;
sigma12 = filter2(window, img1.*img2, 'valid') - mu1_mu2;

if (C1 > 0 & C2 > 0)
   ssim_map = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))./((mu1_sq + mu2_sq + C1).*(sigma1_sq + sigma2_sq + C2));
   cs_map = (2*sigma12 + C2)./(sigma1_sq + sigma2_sq + C2);
else
   numerator1 = 2*mu1_mu2 + C1;
   numerator2 = 2*sigma12 + C2;
	denominator1 = mu1_sq + mu2_sq + C1;
   denominator2 = sigma1_sq + sigma2_sq + C2;

   ssim_map = ones(size(mu1));
   index = (denominator1.*denominator2 > 0);
   ssim_map(index) = (numerator1(index).*numerator2(index))./(denominator1(index).*denominator2(index));
   index = (denominator1 ~= 0) & (denominator2 == 0);
   ssim_map(index) = numerator1(index)./denominator1(index);

   cs_map = ones(size(mu1));
   index = denominator2 > 0;
   cs_map(index) = numerator2(index)./denominator2(index);
end

mssim = mean2(ssim_map);
mcs = mean2(cs_map);

return


function tetrahedron_arbq_rule_test03 ( degree, n, header )

%*****************************************************************************80
%
%% TETRAHEDRON_ARBQ_RULE_TEST03 gets a rule and creates GNUPLOT input files.
%
%  Licensing:
%
%    This code is distributed under the GNU GPL license.
%
%  Modified:
%
%    10 July 2014
%
%  Author:
%
%    Original FORTRAN77 version by Hong Xiao, Zydrunas Gimbutas.
%    This MATLAB version by John Burkardt.
%
%  Reference:
%
%    Hong Xiao, Zydrunas Gimbutas,
%    A numerical algorithm for the construction of efficient quadrature
%    rules in two and higher dimensions,
%    Computers and Mathematics with Applications,
%    Volume 59, 2010, pages 663-676.
%
%  Parameters:
%
%    Input, integer DEGREE, the desired total polynomial degree exactness
%    of the quadrature rule.  0 <= DEGREE <= 15.
%
%    Input, integer N, the number of nodes to be used by the rule.
%
%    Input, string HEADER, an identifier for the filenames.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'TETRAHEDRON_ARBQ_RULE_TEST03\n' );
  fprintf ( 1, '  Get a quadrature rule for the tetrahedron.\n' );
  fprintf ( 1, '  Set up GNUPLOT graphics input.\n' );
  fprintf ( 1, '  Polynomial exactness degree DEGREE = %d\n', degree );
%
%  Retrieve a symmetric quadrature rule.
%
   [ x, w ] = tetrahedron_arbq ( degree, n );
%
%  Create files for input to GNUPLOT.
%
  tetrahedron_arbq_gnuplot ( n, x, header );

  return
end


function node_num = grid_q9_node_num ( nelemx, nelemy )

%*****************************************************************************80
%
%% GRID_Q9_NODE_NUM counts the nodes in a grid of Q9 elements.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    15 February 2006
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NELEMX, NELEMY, the number of elements along the
%    X and Y directions.  The number of elements generated will be
%    NELEMX * NELEMY.
%
%    Output, integer NODE_NUM, the number of nodes in the grid.
%
  node_num = ( 2 * nelemx + 1 ) * ( 2 * nelemy + 1 );

  return
end


% geopdes_inv: the inverse matrix or, for codimension > 0 (rdim > ndim)
% the Moore-Penrose pseudo-inverse.
%
% To be used with msh.geo_map_jac, to compute DF^{-1}.
% In the case of codimension > 0, it computes
%   J = (DF^t * DF)^{-1} * DF^t
%   det = sqrt ( det(DF^t * DF)^{-1} )
%
%  [JinvT, det] = geopdes_invT__ (geo_map_jac)
%
% OUTPUT:
%   Jinv: the computed matrix evaluated at every point. Size (ndim x rdim x nqn x nel)
%   det:  the determinant evaluated at every point. Size (nqn x nel)
%
% Copyright (C) 2010 Carlo de Falco
% Copyright (C) 2015 Rafael Vazquez
%
% 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 Octave; see the file COPYING.  If not, see
% <http://www.gnu.org/licenses/>.

function [Jinv, det] = geopdes_inv__ (v)

  [JinvT, det] = geopdes_invT__ (v);
  Jinv = permute (JinvT, [2, 1, 3:numel(size(v))]);

end

%!test
%! A = [1 2; 3 4];
%! Ainv = geopdes_inv__ (A);
%! assert (Ainv * A, eye(2), 1e-14)
%!
%!test
%! A = [1 2 3; 4 5 6; 7 8 10];
%! Ainv = geopdes_inv__ (A);
%! assert (Ainv * A, eye(3), 1e-14)


% A demo of Bayesian CP factorization for image completion
% Written by Qibin Zhao 2014 RIKEN BSI
%
% In this demo, we provide two algorithms including BCPF_IC and BCPF_MP.
% BCPF_IC is a Bayesian CP for image completion; BCPF_MP is a Bayesian CP
% using mixture priors, which is particularly useful for natural image
% completion. For algorithm settings, please refer to the detailed help by
% >> help BCPF_MP

% The experimental data can be tested with
% 1) Different image files
% 2) Observation rate (1-missing rate)
% The predictive image can be online visualized during model learning.
% The performance of RSE, PSNR, SSIM, Time Cost are evaluated and reported.

close all; clear all;
randn('state',1); rand('state',1); %#ok<RAND>
%% Load image data
filename='./TestImages/peppers.bmp';    % Image file
ObsRatio = 0.1;                      % Observation rate

X = double(imread(filename));
DIM = size(X);

Omega = randperm(prod(DIM));
Omega = Omega(1:round(ObsRatio*prod(DIM)));
O = zeros(DIM);
O(Omega) = 1;
Y = O.*X;

% plot images
subplot = @(m,n,p) subtightplot (m, n, p, [0.01 0.01], [0.01 0.01], [0.01 0.01]);
row =1; col =2;
figure;
subplot(row,col,1);
imshow(uint8(X));
subplot(row,col,2);
imshow(uint8(Y));
drawnow;

% Initialization
TimeCost = zeros(2,1);
RSElist = zeros(2,3);
PSNRlist = zeros(2,1);
SSIMlist = zeros(2,1);
RankEst = zeros(2,1);

%% BCPF for low-rank images completion
tStart = tic;
fprintf('------Bayesian CP factorization for Image Completion---------- \n');
[model] = BCPF_IC(Y, 'obs', O, 'init', 'rand', 'maxRank', 100, 'maxiters', 20, ...
    'tol', 1e-4, 'dimRed', 1, 'verbose', 2);
X_FBCP = double(model.X);
RSElist(1,1) = perfscore(X_FBCP, X);
RSElist(1,2) = perfscore(X_FBCP(O==1), X(O==1));
RSElist(1,3) = perfscore(X_FBCP(O==0), X(O==0));

X_FBCP(O==1) = X(O==1);
PSNRlist(1) = PSNR_RGB(X_FBCP,X);
SSIMlist(1) = ssim_index(rgb2gray(uint8(X_FBCP)),rgb2gray(uint8(X)));
RankEst(1) = model.TrueRank;
TimeCost(1) = toc(tStart);
% figure; imshow(uint8(X_FBCP)); title('FBCP','FontWeight','bold'); drawnow;

%% BCPF-MP (mixture priors) for natural images
if ~isempty(strfind(filename,'facade.bmp'))
    nd=0.1;    % low-rank structural images
else
    nd=1;      % natural images
end
tStart = tic;
fprintf('------Bayesian CP with Mixture Priors for Image Completion---------- \n');
[model] = BCPF_MP(Y, 'obs', O, 'init', 'rand', 'maxRank', 100, 'maxiters', 30, ...
    'tol', 1e-4, 'dimRed', 1, 'verbose', 2, 'nd', nd);
X_FBCPS = double(model.X);

RSElist(2,1) = perfscore(X_FBCPS, X);
RSElist(2,2) = perfscore(X_FBCPS(O==1), X(O==1));
RSElist(2,3) = perfscore(X_FBCPS(O==0), X(O==0));

X_FBCPS(O==1) = X(O==1);
PSNRlist(2) = PSNR_RGB(X_FBCPS,X);
SSIMlist(2) = ssim_index(rgb2gray(uint8(X_FBCPS)),rgb2gray(uint8(X)));
RankEst(2) = model.TrueRank;
TimeCost(2) = toc(tStart);
% figure; imshow(uint8(X_FBCPS)); title('FBCP-MP','FontWeight','bold'); drawnow;

%%
RankEst
RSElist
PSNRlist
SSIMlist
TimeCost


function [ mO ] = ApplySideWindowFiltering( mI, boxRadius, numIterations )
% ----------------------------------------------------------------------------------------------- %
% [ mO ] = ApplySideWindowFiltering( mI, boxRadius, numIterations )
%   Applying Image Edge Preserving Filter using the Side Window Box
%   Filtering algorithm.
% Input:
%   - mI            -   Input Image.
%                       Structure: Image Matrix (Single Channel)
%                       Type: 'Single' / 'Double'.
%                       Range: [0, 1].
%   - boxRadius     -   Box Radius.
%                       Sets the radius of the box filter.
%                       Structure: Scalar.
%                       Type: 'Single' / 'Double'.
%                       Range: {1, 2, 3, ...}.
%   - numIterations -   Number of Iterations.
%                       Sets the number of iterations to apply the filter.
%                       Structure: Scalar.
%                       Type: 'Single' / 'Double'.
%                       Range: {1, 2, 3, ...}.
% Output:
%   - mO            -   Output Image.
%                       Structure: Image Matrix (Single Channel)
%                       Type: 'Single' / 'Double'.
%                       Range: [0, 1].
% References
%   1.  Side Window Filtering (https://arxiv.org/abs/1905.07177).
% Remarks:
%   1.  Can be much faster if written In Place.
% TODO:
%   1.  U.
% Release Notes:
%   -   1.0.000     24/04/2021  Royi Avital     RoyiAvital@yahoo.com
%       *   First release version.
% ----------------------------------------------------------------------------------------------- %

FALSE   = 0;
TRUE    = 1;

OFF     = 0;
ON      = 1;

dataType    = class(mI);
boxLength   = (2 * boxRadius) + 1;

% Atoms of the Filter Bank (Since any of the filters is separable)
vK  = ones(boxLength, 1, dataType) / boxLength;
vKU = ones(boxLength, 1, dataType) / (boxRadius + 1); %<! Up
vKD = ones(boxLength, 1, dataType) / (boxRadius + 1); %<! Down

vKU((boxRadius + 2):boxLength)  = 0;
vKD(1:boxRadius)                = 0;

mO = padarray(mI, [boxRadius, boxRadius], 'both', 'replicate');
mF = zeros(size(mO, 1), size(mO, 2), 8, dataType);
mM = zeros(size(mO), dataType); %<! Minimum Index

for kk = 1:numIterations
    % Written for clarity, not performance
    mF(:, :, 1) = conv2(vK, vKU, mO, 'same'); %<! Left Box
    mF(:, :, 2) = conv2(vK, vKD, mO, 'same'); %<! Right Vox
    mF(:, :, 3) = conv2(vKU, vK, mO, 'same'); %<! Up Box
    mF(:, :, 4) = conv2(vKD, vK, mO, 'same'); %<! Down Box
    mF(:, :, 5) = conv2(vKU, vKU, mO, 'same'); %<! NW Box
    mF(:, :, 6) = conv2(vKU, vKD, mO, 'same'); %<! NE Box
    mF(:, :, 7) = conv2(vKD, vKU, mO, 'same'); %<! SW Box
    mF(:, :, 8) = conv2(vKD, vKD, mO, 'same'); %<! SE Box

    [~, mM(:, :)]   = min(abs(mF - mO), [], 3); %<! Index which minimizes
    for jj = 1:size(mO, 2)
        for ii = 1:size(mO, 1)
            mO(ii, jj) = mF(ii, jj, mM(ii, jj));
        end
    end

end

% Removing boundary
mO = mO((boxRadius + 1):(end - boxRadius), (boxRadius + 1):(end - boxRadius));
mO = min(max(mO, 0), 1);

end


% Stress recovery in plane stress analysis of plates
% Plane stress analysis of a thin plate under tension at its extremes
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
% Warning : On running this the workspace memory will be deleted. Save any
% if any data present before running the code !!
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%--------------------------------------------------------------------------
% Code written by : Siva Srinivas Kolukula                                |
%                   Senior Research Fellow                                |
%                   Structural Mechanics Laboratory                       |
%                   Indira Gandhi Center for Atomic Research              |
%                   India                                                 |
% E-mail : allwayzitzme@gmail.com                                         |
%--------------------------------------------------------------------------
%--------------------------------------------------------------------------
%
% Variable descriptions
%   k = element matrix for stiffness
%   f = element vector
%   stiffness = system matrix
%   force = system vector
%   displacement = system nodal displacement vector
%   coordinates = coordinate values of each node
%   nodes = nodal connectivity of each element
%   index = a vector containing system dofs associated with each element
%   gausspoint = matrix containing sampling points for bending term
%   gaussweight = matrix containing weighting coefficients for bending term
%   bcdof = a vector containing dofs associated with boundary conditions
%   bcval = a vector containing boundary condition values associated with
%           the dofs in 'bcdof'
%   B = matrix for kinematic equation for plane stress
%   D = matrix for material property for plane stress
%   stressGP = stress at the Gauss points
%----------------------------------------------------------------------------

%--------------------------------------------------------------------------
%  input data
%--------------------------------------------------------------------------
clear
clc
disp('Please wait Programme is under Run')
%--------------------------------------------------------------------------
% Input data for nodal coordinate values
%--------------------------------------------------------------------------
load coordinates.dat ;
%--------------------------------------------------------------------------
% Input data for nodal connectivity for each element
%--------------------------------------------------------------------------
load nodes.dat ;

nel = length(nodes) ;                  % number of elements
nnel=4;                                % number of nodes per element
ndof=2;                                % number of dofs per node (UX,UY)
nnode = length(coordinates) ;          % total number of nodes in system
sdof=nnode*ndof;                       % total system dofs
edof=nnel*ndof;                        % degrees of freedom per element

% Units are in SI system
a = 1 ;                           % Length of the plate (along X-axes)
b = 1 ;                           % Length of the plate (along Y-axes)
elementsalongX = 10 ;             % Number of elements along X-axes
elementsalongY = 10 ;

E = 2.1*10^11;                      % Youngs modulus
nu = 0.3;                           % Poisson's ratio
t = 0.0254;                         % plate thickness
rho = 7840. ;                       % Density of the plate

nglx = 2; ngly = 2;         % 2x2 Gauss-Legendre quadrature
nglxy=nglx*ngly;            % number of sampling points per element

%--------------------------------------------------------------------------
% Input data for boundary conditions
%--------------------------------------------------------------------------
% (0,0) and (1,0) are fixed

  bcdof = [ 1 2 241 242] ;
  bcval = zeros(1,length(bcdof)) ;
%--------------------------------------------------------------------------
%  initialization of matrices and vectors
%--------------------------------------------------------------------------

force = zeros(sdof,1);                % system force vector
stiffness = zeros(sdof,sdof);         % system stiffness matrix
displacement = zeros(sdof,1);         % system displacement vector
eldepl = zeros(edof,1) ;              % element displacement vector
index = zeros(edof,1);                % index vector
B = zeros(3,edof);              % kinematic matrix for bending
D = zeros(3,3);                 % constitutive matrix for bending

%--------------------------------------------------------------------------
% force vector
%--------------------------------------------------------------------------
P = 1e5 ;       % Load
rightedge = find(coordinates(:,1)==a);
rightdof = 2*rightedge-ones(length(rightedge),1);
force(rightdof) = -P*b/(elementsalongY+1) ;
leftedge = find(coordinates(:,1)==0);
leftdof = 2*leftedge-ones(length(leftedge),1) ;
force(leftdof) = P*b/(elementsalongY+1) ;

%--------------------------------------------------------------------------
%  computation of element matrices and vectors and their assembly
%--------------------------------------------------------------------------
[Gausspoint,Gaussweight]=GaussQuadrature(nglx);     % sampling points & weights
D = E/(1-nu^2)*[1 nu 0 ; nu 1 0; 0 0 (1-nu)/2] ;    % Constituent Matrix for Plane stress

for iel=1:nel           % loop for the total number of elements

for i=1:nnel
nd(i)=nodes(iel,i);         % extract connected node for (iel)-th element
xx(i)=coordinates(nd(i),1);    % extract x value of the node
yy(i)=coordinates(nd(i),2);    % extract y value of the node
end

k = zeros(edof,edof);        % initialization of stiffness matrix

%--------------------------------------------------------------------------
%  numerical integration for stiffness matrix
%--------------------------------------------------------------------------

for intx=1:nglx
xi = Gausspoint(intx,1);                  % sampling point in x-axis
wtx = Gaussweight(intx,1);               % weight in x-axis
for inty=1:ngly
eta = Gausspoint(inty,1);                  % sampling point in y-axis
wty = Gaussweight(inty,1) ;              % weight in y-axis

[shape,dhdr,dhds] = shapefunctions(xi,eta);     % compute shape functions and
                                    % derivatives at sampling point
jacobian = Jacobian(nnel,dhdr,dhds,xx,yy);  % compute Jacobian

detjacob=det(jacobian);                 % determinant of Jacobian
invjacob=inv(jacobian);                 % inverse of Jacobian matrix

[dhdx,dhdy]=shapefunctionderivatives(nnel,dhdr,dhds,invjacob); % derivatives w.r.t.
                                               % physical coordinate

B=fekineps(nnel,dhdx,dhdy);          % kinematic matrix for stiffness

%--------------------------------------------------------------------------
%  compute element stiffness matrix
%--------------------------------------------------------------------------

k = k+B'*D*B*wtx*wty*detjacob;

end
end                      % end of numerical integration loop for bending term

index = elementdof(nd,nnel,ndof); % extract system dofs associated with element

stiffness = assemble(stiffness,k,index);    % assemble element stiffness matrices

end

%--------------------------------------------------------------------------
%   apply boundary conditions
%--------------------------------------------------------------------------

[stiffness,force] = constraints(stiffness,force,bcdof,bcval);

%--------------------------------------------------------------------------
% Solve the matrix equation
%--------------------------------------------------------------------------
%--------------------------------------------------------------------------
% Solve the matrix equation
%--------------------------------------------------------------------------
displacement = stiffness\force ;

%--------------------------------------------------------------------------
% Stress Recovery
%--------------------------------------------------------------------------
% Calculaing the stress at the Gauss points
[vonmises,stressGP,stressEXP,stressPR,stressG] = StressRecovery(D,displacement);

% Calculating the Element nodal stresses

% Nodal Averaging
type = 'average' ;
% type = 'sum' ;
stressEXPavg = NodalAveraging(stressEXP,type);
stressPRavg = NodalAveraging(stressPR,type) ;

%#### OUTPUTS ####

display('=======SYSTEM MAIN PROPERTIES (isotropic material)=======');
fprintf('       Elasticity  constant  % .2e N/m**2 \n',E);
fprintf('       Poission ratio        % .5f         \n',nu);
fprintf('       thickness of plate    % .5f m       \n',t);
fprintf('       length of plate       % .5f m       \n',a);
fprintf('       breadth of plate      % .5f m       \n',b);
fprintf(' \n ')

display('======SYSTEM GEOMETRICALY PROPERTIES======');
fprintf('        System total element no   % .f \n  ',nel);
fprintf('      System total nodes        % .f \n  ',nnode);
fprintf('      System degree of freedom  % .f \n  ',sdof);

fprintf(' \n ')
% Output of displacements
mytable(displacement,1);

% Output of Stresses at Gauss points
display('======STRESS VALUES AT THE GAUSS POINTS=======')
mytable(stressGP,3);

% Output Von Mises stress
mytable(vonmises,2) ;

% Output of element nodal stresses
display('====ELEMENT NODAL STRESS USING EXTRAPOLATION====')
mytable(stressEXP,3) ;
display('====ELEMENT NODAL STRESS USING PATCH RECOVERY====')
mytable(stressPR,3) ;

% Output of Nodal averaged stresses
display('====NODAL AVERAGED STRESSES OF EXTRAPOLATION====')
mytable(stressEXPavg,4);
display('====NODAL AVERAGED STRESSES OF PATCH RECOVERY====')
mytable(stressPRavg,4);


classdef latticeType < int32
% class representing the different Bravais lattices

  enumeration
    triclinic    (1)
    monoclinic   (2)
    orthorhombic (3)
    trigonal     (4)
    tetragonal   (5)
    hexagonal    (6)
    cubic        (7)
    none         (8)
  end

  methods

    function abg = defaultAngles(this)

      switch this
        case {'trigonal','hexagonal'}
          abg = [90 90 120] * degree;

        otherwise
          abg = [90 90 90] * degree;
      end

    end

    function out = isTriHex(this)

      out = this == latticeType.trigonal || this == latticeType.hexagonal;

    end

    function out = isEucledean(this)

      out = this == latticeType.orthorhombic || ...
        this == latticeType.tetragonal || this == latticeType.cubic;

    end

  end

end


function s = addlogi(s)
%ADDLOGI Add the logistic adistributions.

%   Copyright 1993-2004 The MathWorks, Inc.
%   $Revision: 1.1.6.9 $  $Date: 2004/01/24 09:35:06 $

j = length(s) + 1;
s(j).name = 'Logistic';
s(j).code = 'logistic';
s(j).pnames = {'mu' 'sigma'};
s(j).pdescription = {'location' 'scale'};
s(j).prequired = [false false];
s(j).fitfunc = @logifit;
s(j).likefunc = @logilike;
s(j).cdffunc = @logicdf;
s(j).pdffunc = @logipdf;
s(j).invfunc = @logiinv;
s(j).statfunc = @logistat;
s(j).loginvfunc = [];
s(j).logcdffunc = [];
s(j).hasconfbounds = false;
s(j).censoring = true;
s(j).paramvec = true;
s(j).support = [-Inf Inf];
s(j).closedbound = [false false];
s(j).iscontinuous = true;
s(j).islocscale = true;
s(j).uselogpp = false;

j = j + 1;
s(j).name = 'Log-Logistic';
s(j).code = 'loglogistic';
s(j).pnames = {'mu' 'sigma'};
s(j).pdescription = {'log location' 'log scale'};
s(j).prequired = [false false];
s(j).fitfunc = @loglfit;
s(j).likefunc = @logllike;
s(j).cdffunc = @loglcdf;
s(j).pdffunc = @loglpdf;
s(j).invfunc = @loglinv;
s(j).statfunc = @loglstat;
s(j).loginvfunc = @logiinv;
s(j).logcdffunc = @logicdf;
s(j).hasconfbounds = false;
s(j).censoring = true;
s(j).paramvec = true;
s(j).support = [0 Inf];
s(j).closedbound = [false false];
s(j).iscontinuous = true;
s(j).islocscale = true;
s(j).uselogpp = true;

% ==== Logistic distribution functions ====

% these distribution functions do not yet handle arrays of parameters

function y = logipdf(x, mu, sigma)
%LOGIPDF Logistic probability density function (pdf).
if (nargin<2), mu=0; end
if (nargin<3), sigma=1; end
sigma(sigma <= 0) = NaN;

z = (x - mu) ./ sigma;
k = (z>350); if any(k), z(k) = -z(k); end % prevent Inf/Inf
y = exp(z) ./ ((1 + exp(z)).^2 .* sigma);

function p = logicdf(x, mu, sigma)
%LOGICDF Logistic cumulative distribution function (cdf).
if (nargin<2), mu=0; end
if (nargin<3), sigma=1; end
sigma(sigma <= 0) = NaN;

p = 1 ./ (1 + exp(-(x - mu) ./ sigma));

function x = logiinv(p, mu, sigma)
%LOGIINV Inverse of the logistic cumulative distribution function (cdf).
if (nargin<2), mu=0; end
if (nargin<3), sigma=1; end
sigma(sigma <= 0) = NaN;

x = logit(p).*sigma + mu;

function r = logirnd(mu, sigma, varargin)
%LOGIRND Random arrays from the logistic distribution.
if (nargin<1), mu=0; end
if (nargin<2), sigma=1; end
sigma(sigma <= 0) = NaN;

[err, sizeOut] = statsizechk(2,mu,sigma,varargin{:});
if err > 0
    error('stats:logirnd:InconsistentSizes','Size information is inconsistent.');
end

p = rand(sizeOut);
r = log(p./(1-p)).*sigma + mu;

function [m,v] = logistat(mu, sigma)
%LOGISTAT Mean and variance for the logistic distribution.
if (nargin<1), mu=0; end
if (nargin<2), sigma=1; end
sigma(sigma <= 0) = NaN;

m = mu;
v = sigma.^2 .* pi.^2 ./ 3;

function [nlogL,acov] = logilike(params,data,cens,freq)
%LOGILIKE Negative log-likelihood for the logistic distribution.
if nargin < 4 || isempty(freq), freq = ones(size(data)); end
if nargin < 3 || isempty(cens), cens = zeros(size(data)); end

nlogL = logi_nloglf(params, data, cens, freq);
if nargout > 1
    acov = mlecov(params, data, 'nloglf',@logi_nloglf, 'cens',cens, 'freq',freq);
end

% ==== Logistic fitting functions ====

function [phat,pci] = logifit(x,alpha,cens,freq,opts)
%LOGIFIT Parameter estimates and confidence intervals for logistic data.

if nargin < 2 || isempty(alpha), alpha = .05; end
if nargin < 3 || isempty(cens), cens = zeros(size(x)); end
if nargin < 4 || isempty(freq), freq = ones(size(x)); end
if nargin < 5, opts = []; end

% Moment estimators as starting point
xunc = x(cens == 0);
start = [mean(xunc) std(xunc).*sqrt(3)./pi];

% The default options include turning statsfminbx's display off.  This
% function gives its own warning/error messages, and the caller can turn
% display on to get the text output from statsfminbx if desired.
options = statset(statset('logifit'), opts);
tolBnd = options.TolBnd;
options = optimset(options);
dfltOptions = struct('DerivativeCheck','off', 'HessMult',[], ...
    'HessPattern',ones(2,2), 'PrecondBandWidth',Inf, ...
    'TypicalX',ones(2,1), 'MaxPCGIter',1, 'TolPCG',0.1);

% Maximize the log-likelihood with respect to mu and sigma.
funfcn = {'fungrad' 'logifit' @logi_nloglf [] []};
[phat, nll, lagrange, err, output] = ...
         statsfminbx(funfcn, start, [-Inf; tolBnd], [Inf; Inf], ...
                     options, dfltOptions, 1, x, cens, freq);
if (err == 0)
    % statsfminbx may print its own output text; in any case give something
    % more statistical here, controllable via warning IDs.
    if output.funcCount >= options.MaxFunEvals
        wmsg = 'Maximum likelihood estimation did not converge.  Function evaluation limit exceeded.';
    else
        wmsg = 'Maximum likelihood estimation did not converge.  Iteration limit exceeded.';
    end
    warning('stats:logifit:IterOrEvalLimit',wmsg);
elseif (err < 0)
    error('stats:logifit:NoSolution',...
          'Unable to reach a maximum likelihood solution.');
end

if nargout > 1
    acov = mlecov(phat, x, 'nloglf',@logi_nloglf, 'cens',cens, 'freq',freq);
    probs = [alpha/2; 1-alpha/2];
    se = sqrt(diag(acov))';

    % Compute the CI for mu using a normal approximation for muhat.
    pci(:,1) = norminv(probs, phat(1), se(1));

    % Compute the CI for sigma using a normal approximation for
    % log(sigmahat), and transform back to the original scale.
    % se(log(sigmahat)) is se(sigmahat) / sigmahat.
    logsigci = norminv(probs, log(phat(2)), se(2)./phat(2));
    pci(:,2) = exp(logsigci);
end

function [nll,ngrad] = logi_nloglf(parms, x, cens, freq)
%LOGI_NLOGLF Objective function for logistic maximum likelihood.
mu = parms(1);
sigma = parms(2);
z = (x - mu) ./ sigma;
logitz = 1 ./ (1 + exp(-z));
clogitz = 1 ./ (1 + exp(z));
logclogitz = log(clogitz);
k = (z > 700); if any(k), logclogitz(k) = z(k); end % fix intermediate overflow

L = z + 2.*logclogitz - log(sigma);
ncen = sum(freq.*cens);
if ncen > 0
    cen = (cens == 1);
    L(cen) = logclogitz(cen);
end
nll = -sum(freq .* L);

if nargout > 1
    t = (2.*logitz - 1) ./ sigma;
    dL1 = t;
    dL2 = z.*t - 1./sigma;
    if ncen > 0
        t = logitz(cen) ./ sigma;
        dL1(cen) = t;
        dL2(cen) = z(cen) .* t;
    end
    ngrad = -[sum(freq .* dL1) sum(freq .* dL2)];
end

% ==== Log-Logistic distribution functions ====

% these distribution functions do not yet handle arrays of parameters

function y = loglpdf(x, mu, sigma)
%LOGLPDF Log-logistic probability density function (pdf).
if (nargin<2), mu=0; end
if (nargin<3), sigma=1; end
sigma(sigma <= 0) = NaN;

nonpos = (x <= 0);
x(nonpos) = realmin;
z = (log(x) - mu) ./ sigma;
c = ones(size(z));
k = (z>350); % prevent Inf/Inf
if any(k)
    z(k) = -z(k);
    c(k) = -1;
end
y = exp(z.*(1-c.*sigma) - mu) ./ ((1 + exp(z)).^2 .* sigma);
y(nonpos) = 0;
% the first and third of these would happen automatically for x==0, but
% generate LogOfZero warnings.  the second would be NaN.
y(x==0 & sigma<1) = 0;
y(x==0 & sigma==1) = 1;
y(x==0 & sigma>1) = Inf;

function p = loglcdf(x, mu, sigma)
%LOGLCDF Log-logistic cumulative distribution function (cdf).
if (nargin<2), mu=0; end
if (nargin<3), sigma=1; end
sigma(sigma <= 0) = NaN;

nonpos = (x <= 0);
x(nonpos) = realmin;
p = 1 ./ (1 + exp(-(log(x) - mu) ./ sigma));
% this would happen automatically for x==0, but generates LogOfZero warnings
p(nonpos) = 0;

function x = loglinv(p, mu, sigma)
%LOGLINV Inverse of the log-logistic cumulative distribution function (cdf).
if (nargin<2), mu=0; end
if (nargin<3), sigma=1; end
sigma(sigma <= 0) = NaN;

x = exp(logit(p).*sigma + mu);

function r = loglrnd(mu, sigma, varargin)
%LOGLRND Random arrays from the log-logistic distribution.
if (nargin<1), mu=0; end
if (nargin<2), sigma=1; end
sigma(sigma <= 0) = NaN;

[err, sizeOut] = statsizechk(2,mu,sigma,varargin{:});
if err > 0
    error('stats:loglrnd:InconsistentSizes','Size information is inconsistent.');
end

p = rand(sizeOut);
r = exp(log(p./(1-p)).*sigma + mu);

function [m,v] = loglstat(mu, sigma)
%LOGLSTAT Mean and variance for the log-logistic distribution.
if (nargin<1), mu=0; end
if (nargin<2), sigma=1; end
sigma(sigma <= 0) = NaN;

if sigma < 1
    m = exp(mu + gammaln(1+sigma) + gammaln(1-sigma));
else
    m = Inf;
end
if sigma < .5
    v = exp(2.*mu + gammaln(1+2.*sigma) + gammaln(1-2.*sigma)) - m.^2;
else
    v = Inf;
end

function [nlogL,acov] = logllike(params,data,cens,freq)
%LOGLLIKE Negative log-likelihood for the log-logistic distribution.
if nargin < 4 || isempty(freq), freq = ones(size(data)); end
if nargin < 3 || isempty(cens), cens = zeros(size(data)); end

nlogL = logl_nloglf(params, data, cens, freq);
if nargout > 1
    acov = mlecov(params, data, 'nloglf',@logl_nloglf, 'cens',cens, 'freq',freq);
end

% ==== Log-Logistic fitting functions ====

function [phat,pci] = loglfit(x,alpha,cens,freq,opts)
%LOGLFIT Parameter estimates and confidence intervals for log-logistic data.

if nargin < 2 || isempty(alpha), alpha = .05; end
if nargin < 3 || isempty(cens), cens = zeros(size(x)); end
if nargin < 4 || isempty(freq), freq = ones(size(x)); end
if nargin < 5, opts = []; end

if any(x <= 0)
    error('stats:loglfit:BadData','The data in X must be positive');
end

% Moment estimators as starting point
logxunc = log(x(cens == 0));
start = [mean(logxunc) std(logxunc).*sqrt(3)./pi];

% The default options include turning statsfminbx's display off.  This
% function gives its own warning/error messages, and the caller can turn
% display on to get the text output from statsfminbx if desired.
options = statset(statset('loglfit'), opts);
tolBnd = options.TolBnd;
options = optimset(options);
dfltOptions = struct('DerivativeCheck','off', 'HessMult',[], ...
    'HessPattern',ones(2,2), 'PrecondBandWidth',Inf, ...
    'TypicalX',ones(2,1), 'MaxPCGIter',1, 'TolPCG',0.1);

% Maximize the log-likelihood with respect to mu and sigma.
funfcn = {'fungrad' 'loglfit' @logl_nloglf [] []};
[phat, nll, lagrange, err, output] = ...
         statsfminbx(funfcn, start, [-Inf; tolBnd], [Inf; Inf], ...
                     options, dfltOptions, 1, x, cens, freq);
if (err == 0)
    % statsfminbx may print its own output text; in any case give something
    % more statistical here, controllable via warning IDs.
    if output.funcCount >= options.MaxFunEvals
        wmsg = 'Maximum likelihood estimation did not converge.  Function evaluation limit exceeded.';
    else
        wmsg = 'Maximum likelihood estimation did not converge.  Iteration limit exceeded.';
    end
    warning('stats:loglfit:IterOrEvalLimit',wmsg);
elseif (err < 0)
    error('stats:loglfit:NoSolution',...
          'Unable to reach a maximum likelihood solution.');
end

if nargout > 1
    acov = mlecov(phat, x, 'nloglf',@logi_nloglf, 'cens',cens, 'freq',freq);
    probs = [alpha/2; 1-alpha/2];
    se = sqrt(diag(acov))';

    % Compute the CI for mu using a normal approximation for muhat.
    pci(:,1) = norminv(probs, phat(1), se(1));

    % Compute the CI for sigma using a normal approximation for
    % log(sigmahat), and transform back to the original scale.
    % se(log(sigmahat)) is se(sigmahat) / sigmahat.
    logsigci = norminv(probs, log(phat(2)), se(2)./phat(2));
    pci(:,2) = exp(logsigci);
end

function [nll,ngrad] = logl_nloglf(parms, x, cens, freq)
%LOGL_NLOGLF Objective function for log-logistic maximum likelihood.
mu = parms(1);
sigma = parms(2);
logx = log(x);
z = (logx - mu) ./ sigma;
logitz = 1 ./ (1 + exp(-z));
clogitz = 1 ./ (1 + exp(z));
logclogitz = log(clogitz);
k = (z > 700); if any(k), logclogitz(k) = z(k); end % fix intermediate overflow

L = z + 2.*logclogitz - log(sigma) - logx;
ncen = sum(freq.*cens);
if ncen > 0
    cen = (cens == 1);
    L(cen) = logclogitz(cen);
end
nll = -sum(freq .* L);

if nargout > 1
    t = (2.*logitz - 1) ./ sigma;
    dL1 = t;
    dL2 = z.*t - 1./sigma;
    if ncen > 0
        t = logitz(cen) ./ sigma;
        dL1(cen) = t;
        dL2(cen) = z(cen) .* t;
    end
    ngrad = -[sum(freq .* dL1) sum(freq .* dL2)];
end

% ==== utility functions ====

function logitp = logit(p)
%LOGIT Logistic transformation, handling edge and out of range.
logitp = repmat(NaN,size(p));
logitp(p==0) = -Inf;
logitp(p==1) = Inf;
ok = (0<p & p<1);
logitp(ok) = log(p(ok)./(1-p(ok)));


%% element2lattice
% Below is a demonstration of the features of the |element2lattice| function

%%
clear; close all; clc;

%% Syntax
% |[Es,Vs,Cs]=element2lattice(E,V,cPar);|

%% Description
% This function converts an element description (elements and vertices i.e
% nodes into a lattice structure. The lattice structure is returned as a
% hexahederal mesh.

%% Examples
%

%%
% Plot settings
cMap=gjet(4);
fontSize=15;

%% Example: Creating a lattice structure on a hexahedral element

%%
% Creating example geometry.
boxDim=[1 1 1];
boxEl=[1 1 1];
[meshStruct]=hexMeshBox(boxDim,boxEl);
E=meshStruct.E;
V=meshStruct.V;
F=meshStruct.F;
[indBoundary]=tesBoundary(F);

%%
% Create lattice structure
controlParameter.shrinkFactor=0.2; %Strut sides are formed by shrinking the input mesh faces by this factor
controlParameter.numDigitKeep=5; %used for merging nodes
controlParameter.meshType='quad'; %desired output mesh type
controlParameter.indBoundary=indBoundary; %indices of the boundary faces
controlParameter.latticeSide=1; %1=side 1 the edge lattice, 2=side 2 the dual lattice to the edge lattice
[Fs1,Vs1,Cs1]=element2lattice(E,V,controlParameter); %Get lattice structure

%%
% Visualizing input mesh and lattice structures

cFigure;
title('An edge lattice structure on a hexahedral element','fontSize',fontSize)
hold on;
hp1=gpatch(F,V,0.5*ones(1,3),'k',0.25,4);
hp2=gpatch(Fs1(Cs1==0,:),Vs1,Cs1(Cs1==0));
hp3=gpatch(Fs1(Cs1==1,:),Vs1,Cs1(Cs1==1));
legend([hp1,hp2,hp3],'Input mesh','Inner faces','Boundary faces')
colormap(cMap);
cLim=caxis;
axisGeom(gca,fontSize);
camlight headlight; lighting flat;

drawnow;

%% Example: Different lattice sides

%%
% Creating example geometry.
boxDim=[1 1 1];
boxEl=[1 1 1];
[meshStruct]=hexMeshBox(boxDim,boxEl);
E=meshStruct.E;
V=meshStruct.V;
F=meshStruct.F;
[indBoundary]=tesBoundary(F);

%%
% Compute other "lattice side"
controlParameter.shrinkFactor=0.2; %Strut sides are formed by shrinking the input mesh faces by this factor
controlParameter.numDigitKeep=5; %used for merging nodes
controlParameter.meshType='quad'; %desired output mesh type
controlParameter.indBoundary=indBoundary; %indices of the boundary faces
controlParameter.latticeSide=2; %1=side 1 the edge lattice, 2=side 2 the dual lattice to the edge lattice
[Fs2,Vs2,Cs2]=element2lattice(E,V,controlParameter); %Get lattice structure

%%
% Visualizing input mesh and lattice structures

cFigure;
hs=subplot(2,2,1);
title('The input mesh','fontSize',fontSize)
hold on;
gpatch(F,V,0.5*ones(1,3),'k',0.25,3);
axisGeom(gca,fontSize);
camlight headlight; lighting flat;
ha=axis; axis off;

subplot(2,2,2);
title('The two complementary lattice structures','fontSize',fontSize)
hold on;
gpatch(F,V,0.5*ones(1,3),'k',0.25,3);
gpatch(Fs1,Vs1,Cs1);
gpatch(Fs2,Vs2,Cs2);
colormap(cMap);
cLim=caxis;
axisGeom(gca,fontSize);
camlight headlight; lighting flat;
axis(ha); axis off;

subplot(2,2,3);
title('Lattice side 1','fontSize',fontSize)
hold on;
gpatch(F,V,0.5*ones(1,3),'k',0.25,3);
gpatch(Fs1,Vs1,Cs1);
colormap(cMap);
caxis(cLim);
axisGeom(gca,fontSize);
camlight headlight; lighting flat;
axis(ha); axis off;

subplot(2,2,4);
title('Lattice side 2','fontSize',fontSize)
hold on;
gpatch(F,V,0.5*ones(1,3),'k',0.25,3);
gpatch(Fs2,Vs2,Cs2);
colormap(cMap);
caxis(cLim);
axisGeom(gca,fontSize);
camlight headlight; lighting flat;
axis(ha); axis off;

drawnow;

%% Example: Refining the lattice structure

%%
% Refining a lattice structure by first refining the input mesh

cFigure;
gtitle('Lattice structure refinement',fontSize);

for q=1:4
    if q>1
        [E,V]=subHex(E,V,1); %Refine input mesh
        [F]=element2patch(E); %Patch data for plotting
        [indBoundary]=tesBoundary(F);
        controlParameter.indBoundary=indBoundary; %indices of the boundary faces
    end

    [Fs1,Vs1,Cs1]=element2lattice(E,V,controlParameter); %Get lattice structure

    % Visualizing input mesh and lattice structures

    subplot(2,2,q);
    hold on; title(['Split iteration ',num2str(q)]);
    gpatch(Fs1,Vs1,Cs1);
    colormap(cMap);
    axisGeom(gca,fontSize);
    camlight headlight; lighting flat;

end
drawnow;

%% Example: Creating a lattice structure on tetrahedral elements

%%
% Creating example geometry.

[V,~]=platonic_solid(1,1); %A single tetrahedron
E=[2 3 4 1]; %The element description
[E,V]=subTet(E,V,1); %Refine the tetrahedron once
[F]=element2patch(E); %Patch data for plotting
[indBoundary]=tesBoundary(F); %Get boundary face indices

%%
% Create lattice structure
controlParameter.shrinkFactor=0.2; %Strut sides are formed by shrinking the input mesh faces by this factor
controlParameter.numDigitKeep=5; %used for merging nodes
controlParameter.meshType='quad'; %desired output mesh type
controlParameter.indBoundary=indBoundary; %indices of the boundary faces

controlParameter.latticeSide=1; %1=side 1 the edge lattice, 2=side 2 the dual lattice to the edge lattice
[Fs1,Vs1,Cs1]=element2lattice(E,V,controlParameter); %Get lattice structure

%%
% Visualizing input mesh and lattice structures

cFigure;
title('An edge lattice structure on a hexahedral element','fontSize',fontSize)
hold on;
hp1=gpatch(F,V,0.5*ones(1,3),'k',0.25,4);
hp2=gpatch(Fs1(Cs1==0,:),Vs1,Cs1(Cs1==0));
hp3=gpatch(Fs1(Cs1==1,:),Vs1,Cs1(Cs1==1));
legend([hp1,hp2,hp3],'Input mesh','Inner faces','Boundary faces')
colormap(cMap);
cLim=caxis;
axisGeom(gca,fontSize);
camlight headlight; lighting flat;

drawnow;

%% Example: Different lattice sides

%%
% Compute other "lattice side"
controlParameter.latticeSide=2; %1=side 1 the edge lattice, 2=side 2 the dual lattice to the edge lattice
[Fs2,Vs2,Cs2]=element2lattice(E,V,controlParameter); %Get lattice structure

%%
% Visualizing input mesh and lattice structures

cFigure;
hs=subplot(2,2,1);
title('The input mesh','fontSize',fontSize)
hold on;
gpatch(F,V,0.5*ones(1,3),'k',0.25,3);
axisGeom(gca,fontSize);
camlight headlight; lighting flat;
ha=axis; axis off;

subplot(2,2,2);
title('The two complementary lattice structures','fontSize',fontSize)
hold on;
gpatch(F,V,0.5*ones(1,3),'k',0.25,3);
gpatch(Fs1,Vs1,Cs1);
gpatch(Fs2,Vs2,Cs2);
colormap(cMap);
cLim=caxis;
axisGeom(gca,fontSize);
camlight headlight; lighting flat;
axis(ha); axis off;

subplot(2,2,3);
title('Lattice side 1','fontSize',fontSize)
hold on;
gpatch(F,V,0.5*ones(1,3),'k',0.25,3);
gpatch(Fs1,Vs1,Cs1);
colormap(cMap);
caxis(cLim);
axisGeom(gca,fontSize);
camlight headlight; lighting flat;
axis(ha); axis off;

subplot(2,2,4);
title('Lattice side 2','fontSize',fontSize)
hold on;
gpatch(F,V,0.5*ones(1,3),'k',0.25,3);
gpatch(Fs2,Vs2,Cs2);
colormap(cMap);
caxis(cLim);
axisGeom(gca,fontSize);
camlight headlight; lighting flat;
axis(ha); axis off;

drawnow;

%% Example: Changing lattice strut thickness (and porosity)

%%
% The strut thickness of the lattice depends on the shrinkfactor.

% Create lattice structure
controlParameter.numDigitKeep=5; %used for merging nodes
controlParameter.meshType='quad'; %desired output mesh type
controlParameter.indBoundary=indBoundary; %indices of the boundary faces

shrinkFactorSet=linspace(0.1,0.5,4);

for latticeSide=1:2
    controlParameter.latticeSide=latticeSide;
    cFigure;
    gtitle('Lattice structure porosity control',fontSize);

    for q=1:4
        controlParameter.shrinkFactor=shrinkFactorSet(q); %Strut sides are formed by shrinking the input mesh faces by this facto

        [Fs1,Vs1,Cs1]=element2lattice(E,V,controlParameter); %Get lattice structure

        % Visualizing input mesh and lattice structures
        subplot(2,2,q);
        hold on; title(['Shrink factor ',num2str(pround(shrinkFactorSet(q),3))]);
        gpatch(Fs1,Vs1,Cs1);
        colormap(cMap);
        axisGeom(gca,fontSize);
        camlight headlight; lighting flat;
    end
    drawnow;
end

%% Example: Changing output surface mesh type

%%
% Different output mesh types are available i.e. quadrilateral and
% triangular faces and also hexahedral elements.

meshTypeSet={'quad','tri'};

boxDim=[1 1 1];
boxEl=[1 1 1];
[meshStruct]=hexMeshBox(boxDim,boxEl);
E=meshStruct.E;
V=meshStruct.V;
F=meshStruct.F;
[indBoundary]=tesBoundary(F);

clear controlParameter

% Create lattice structure
controlParameter.latticeSide=2;
controlParameter.numDigitKeep=5; %used for merging nodes
controlParameter.indBoundary=indBoundary; %indices of the boundary faces
controlParameter.shrinkFactor=0.3;

cFigure;
gtitle('Lattice structure mesh output type control',fontSize);

for q=1:numel(meshTypeSet)
    controlParameter.meshType=meshTypeSet{q}; %The current mesh type

    [Fs1,Vs1,Cs1]=element2lattice(E,V,controlParameter); %Get lattice structure

    % Visualizing input mesh and lattice structures
    subplot(1,numel(meshTypeSet),q);
    hold on; title(['Mesh type ',meshTypeSet{q}]);
    gpatch(Fs1,Vs1,Cs1,'k',0.8,2);
    colormap(gjet(250));
    axisGeom(gca,fontSize);
    camlight headlight; lighting flat;
end
drawnow;

%% Example: Exporting hexahedral elements instead of surface elements

%%
% The |element2patch| function can also be used to export hexahedral
% elements directly by setting the meshType parameter to 'hex'.
% Furthermore, it is possible to subdevide the ellongated hexahedral
% elements using a certain number of split iterations. Below is an example
% of hexahedral element output with increasing number of split iterations
% used for the ellongated hexahedral elements.

boxDim=[1 1 1];
boxEl=[1 1 1];
[meshStruct]=hexMeshBox(boxDim,boxEl);
E=meshStruct.E;
V=meshStruct.V;
F=meshStruct.F;
[indBoundary]=tesBoundary(F);

clear controlParameter

% Create lattice structure
controlParameter.latticeSide=2;
controlParameter.numDigitKeep=5; %used for merging nodes
controlParameter.indBoundary=indBoundary; %indices of the boundary faces
controlParameter.shrinkFactor=0.25;
controlParameter.meshType='hex';

hexSplitSet=[0 1 2];
cFigure;
gtitle('Lattice structure hex element output and element mesh refinement',fontSize);

c=1; %counter for plotting
for latticeSide=1:2
    controlParameter.latticeSide=latticeSide;
    for q=1:numel(hexSplitSet)
        controlParameter.hexSplit=hexSplitSet(q); %The current mesh type

        [Es1,Vs1,Cs1]=element2lattice(E,V,controlParameter); %Get lattice structure

        [Fs1,Cs1F]=element2patch(Es1,Cs1); %Patch data for plotting

        % Visualizing input mesh and lattice structures
        subplot(2,numel(hexSplitSet),c);
        hold on; title(['hexSplit=',num2str(hexSplitSet(q))]);
        gpatch(Fs1,Vs1,Cs1F,'k',0.8,2);
        colormap(gjet(250));
        axisGeom(gca,fontSize);
        camlight headlight; lighting flat;
        c=c+1;
    end
end
drawnow;

%%
%

[V,F]=platonic_solid(1,1);
E=[1 2 4 3];

%
% boxDim=[1 1 1];
% boxEl=[1 1 1];
% [meshStruct]=hexMeshBox(boxDim,boxEl);
% E=meshStruct.E;
% V=meshStruct.V;
% F=meshStruct.F;
[indBoundary]=tesBoundary(F);

clear controlParameter

% Create lattice structure
controlParameter.latticeSide=2;
controlParameter.numDigitKeep=5; %used for merging nodes
controlParameter.indBoundary=indBoundary; %indices of the boundary faces
controlParameter.shrinkFactor=0.25;
controlParameter.meshType='hex';

hexSplitSet=[0 1 2];
cFigure;
gtitle('Lattice structure hex element output and element mesh refinement',fontSize);

c=1; %counter for plotting
for latticeSide=1:2
    controlParameter.latticeSide=latticeSide;
    for q=1:numel(hexSplitSet)
        controlParameter.hexSplit=hexSplitSet(q); %The current mesh type

        [Es1,Vs1,Cs1]=element2lattice(E,V,controlParameter); %Get lattice structure

        [Fs1,Cs1F]=element2patch(Es1,Cs1); %Patch data for plotting

        % Visualizing input mesh and lattice structures
        subplot(2,numel(hexSplitSet),c);
        hold on; title(['hexSplit=',num2str(hexSplitSet(q))]);
        gpatch(Fs1,Vs1,Cs1F,'k',0.8,2);
        colormap(gjet(250));
        axisGeom(gca,fontSize);
        camlight headlight; lighting flat;
        c=c+1;
    end
end
drawnow;

%% Example: Creating lattice structure variations through input mesh conversion/subdevission

cPar.shrinkFactor=0.25;
cPar.numDigitKeep=5;
cPar.meshType='quad';

cParSmooth.Method='HC';
cParSmooth.n=10;

cFigure;
c=1;
for latticeSide=1:2
    for testCase=1:3

        switch testCase
            case 1
                boxEl=[1 1 1];
                [meshStruct]=hexMeshBox(boxDim,boxEl);
                E=meshStruct.E;
                V=meshStruct.V;
                F=meshStruct.F;
                Ft=F;
                Vt=V;
                [E,V]=subHex(E,V,1,1);
            case 2
                boxEl=[1 1 1];
                [meshStruct]=hexMeshBox(boxDim,boxEl);
                E=meshStruct.E;
                V=meshStruct.V;
                F=meshStruct.F;
                Ft=F;
                Vt=V;
                [E,V]=subHex(E,V,1,1);
                [E,V,~]=hex2tet(E,V,[],1);
            case 3
                boxEl=[1 1 1];
                [meshStruct]=hexMeshBox(boxDim,boxEl);
                E=meshStruct.E;
                V=meshStruct.V;
                F=meshStruct.F;
                Ft=F;
                Vt=V;
                [E,V,~]=hex2tet(E,V,[],1);
                [E,V]=tet2hex(E,V);
        end

        %Get boundary indices
        [F]=element2patch(E); %Patch data for plotting
        [indBoundary]=tesBoundary(F); %Boundary indices

        %Compute lattice
        cPar.latticeSide=latticeSide;
        cPar.indBoundary=indBoundary;
        [Fn,Vn,Cn]=element2lattice(E,V,cPar);

%         %Refine mesh
%         [Fn,Vn]=subQuad(Fn,Vn,1);
%         Cn=repmat(Cn,[4 1]); %Replicate color info
%
%         %Smoothen
%         indRigid=Fn(Cn==1,:);
%         indRigid=unique(indRigid(:)); %Indices for boundary elements to hold on to
%         cParSmooth.RigidConstraints=indRigid;
%         if cParSmooth.n>0
%             [Vn]=tesSmooth(Fn,Vn,[],cParSmooth); %Smoothen mesh
%         end

        %Visualize
        subplot(2,3,c); hold on;
        gpatch(Ft,Vt,0.5*ones(1,3),'k',0.25);
        gpatch(Fn,Vn,Cn,'k',1);
        colormap(cMap);
        axisGeom(gca,fontSize);
        camlight headlight;
        lighting flat;

        c=c+1;
    end
end
drawnow;

%% Example: Create lattice structures on arbitry input meshes
% Below is an example of a general tetrahedral mesh

%%

testCase=2;
switch testCase
    case 1
        [F,V,~]=geoSphere(2,1); % Building a geodesic dome surface model
    case 2
        [F,V]=stanford_bunny('g'); %Bunny
        V_mean=mean(V,1);
        V=V-V_mean(ones(size(V,1),1),:);
end

%%
% Using tetgen to create a tetrahedral mesh (see |HELP_runTetGen|)

stringOpt='-pq1.2AaY';

inputStruct.stringOpt=stringOpt;
inputStruct.Faces=fliplr(F);
inputStruct.Nodes=V;
inputStruct.holePoints=[];
inputStruct.faceBoundaryMarker=ones(size(F,1),1); %Face boundary markers
inputStruct.regionPoints=getInnerPoint(F,V); %region points
inputStruct.regionA=tetVolMeanEst(F,V);
inputStruct.minRegionMarker=2; %Minimum region marker

[meshOutput]=runTetGen(inputStruct); %Run tetGen

Fb=meshOutput.facesBoundary;
Cb=meshOutput.boundaryMarker;
V=meshOutput.nodes;
CE=meshOutput.elementMaterialID;
E=meshOutput.elements;
F=meshOutput.faces;
[indBoundary]=tesBoundary(F); %Boundary indices

%%
% Create lattice structure

% Define spatially varying (on nodes) shrink factor
s=V(:,1);
s=s-min(s(:));
s=s./max(s(:));
s=(s*0.6)+0.05;

clear controlParameter
controlParameter.shrinkFactor=s; %Strut sides are formed by shrinking the input mesh faces by this factor
controlParameter.numDigitKeep=5; %used for merging nodes
controlParameter.meshType='quad'; %desired output mesh type
controlParameter.indBoundary=indBoundary; %indices of the boundary faces
controlParameter.latticeSide=1; %1=side 1 the edge lattice, 2=side 2 the dual lattice to the edge lattice

[Fs1,Vs1,Cs1]=element2lattice(E,V,controlParameter); %Get lattice structure

%%
% PLOTTING MODEL

%Selecting half of the model to see interior
Y=V(:,2); YE=mean(Y(E),2);
logicCutView=YE>mean(Y);
[Fs,Cs]=element2patch(E(logicCutView,:),CE(logicCutView),'tet4');

cFigure;
hold on;
title('Cut view of tetrahedral mesh model','FontSize',fontSize);
gpatch(Fb,V,0.5*ones(1,3),'none',0.5);
gpatch(Fs,V,Cs,'k',1);
camlight headlight;
axisGeom(gca,fontSize);
axis off;
colormap(cMap);
drawnow;

%%
% Visualize lattice structure
cFigure;
hold on;
title('Lattice structure on arbitrary input mesh','FontSize',fontSize);
% gpatch(Fb,V,0.5*ones(1,3),'none',0.5);
gpatch(Fs1,Vs1,Cs1,'none',1);
camlight headlight; lighting flat;
axisGeom(gca,fontSize);
axis off;
colormap(cMap);
drawnow;

%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>

%%
% _*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/>.


%Corrects the navigation states using the Kalman generated error values
%if size(att)=[4,1], it assumes att=qbn else it assumes att=Cbn
%vel=Vn
%dx=corrections where position errors are defined as dr^n (order:[pos, vel, att])
%delta_x(k)=K(delta_y(k)-h(delta_x(k|k-1)))=K*delta_y(k)

function [att_new, Ve_new, ecef_new]=correctnav_eframe_v000(att, Ve, ecef, dx)

%Correct the position
ecef_new=ecef-dx(1:3);

%velocity correction
Ve_new=Ve-dx(4:6);

%attitude correction
if (size(att,1)==3) %attitude is represented as DCM
    Cet=rot2dcm_v000(dx(7:9));   %Erroneous to true navigation frame:DCM=(I+S(ang))
    att_new=Cet*att;
else %attitude is quaternion
    %Method I
    qet=rvec2quat_v000(dx(7:9));
    att_new=quatmult_v000(qet, att);

%     %Method II:
%     %Method I involves some trigonometric functions. Here is a
%     %algebraic correction method (See: Chung 1996-Eq:16) (Special thanks to Kaygisiz)
%     R=[-q(2) -q(3) -q(4);q(1) q(4) -q(3);q(-4) q(1) q(2);q(3) q(-2) q(1)];
%     dq=-0.5*R*dx(7:9);
%     att_new=att_new-dq;
%     att_new=att_new/norm(att_new);  %optional
end


function [k,a]=invkini(x,y,phi)
global   l1 l2 l3
 l1=1;l2=1;l3=0.5;
xx=x-l3*cos(phi);
yy=y-l3*sin(phi);
k=(xx^2+yy^2-l1^2-l2^2)/(2*l1*l2);
if k > 1
    a=[];
    return
end
t2=acos(k);
d=l1^2+l2^2+2*l1*l2*cos(t2);
cc=(xx*(l1+l2*cos(t2))+yy*l2*sin(t2))/d;
ss=(-xx*l2*sin(t2)+yy*(l1+l2*cos(t2)))/d;
t1=atan2(ss,cc);
t3=phi-t1-t2;
a=[ t1 t2 t3];


function [rResult] = relm_HypothesisTest(vRatesH, vRatesN, nNumberSimulation, fMagThreshold)
%RELMTEST
%script to test two earthqauake rate hypotheses using earthquake data
%
% park1=vRatesH;
% park2=vRatesN;
% clear test null;
%     xmin(i)=park1(j,1);
%     xmax(i)=park1(j,2);
%     ymin(i)=park1(j,3);
%     ymax(i)=park1(j,4);
%     zmin(i)=park1(j,5);
%     zmax(i)=park1(j,6);
%     magmin(i)=park1(j,7);
%     magmax(i)=park1(j,8);
%     lamda1(i)=park1(j,9);
%     weight(i)=park1(j,10);
% Get the numbers of observed earthquakes per bin
vNumberQuake = vRatesH(:,11);
% Get the lower magnitude-limits per bin
vMagMin = vRatesH(:,7);
% Get the forecasted numbers of events per bin
vLambdaH = vRatesH(:,9);
vLambdaN = vRatesN(:,9);
% Get the weightings per bins
vWeightH = vRatesH(:,10);
vWeightN = vRatesN(:,10);
vWeightCombined = vWeightH .* vWeightN .* (vMagMin > fMagThreshold);

% Remove rows of matrix for which weight is zero
[nRow, nColumn] = size(vRatesH);
nNewIndex = 0;
for nCnt = 1:nRow
  if vWeightCombined(nCnt)>0
    nNewIndex = nNewIndex + 1;
    vWeight(nNewIndex) = vWeightCombined(nCnt);
    vNumberQuakeSel(nNewIndex) = vNumberQuake(nCnt);
    vLambdaHSel(nNewIndex) = vLambdaH(nCnt);
    vLambdaNSel(nNewIndex) = vLambdaN(nCnt);
    mmin(nNewIndex) = vMagMin(nCnt);
  end
end

% Get the number of events (weighted)
vNumberQuake = vWeight .* vNumberQuakeSel;
nNumberQuake = sum(vNumberQuake);

% Weight the important columns
vLambdaH = vWeight .* vLambdaHSel;
vLambdaN = vWeight .* vLambdaNSel;
% Garbage collection
clear vRatesH vRatesN vLambdaHSel vLambdaNSel vNumberQuakeSel vMagMin vWeightCombined vWeightH vWeightN;

%make a weighted magnitude-frequency plot
%
% mf=[mmin;vNumberQuake;vLambdaH;vLambdaN]';
% mfsort=sortrows(mf);
% mag=mfsort(:,1);
%
% Fobs=flip(cumsum(flip(mfsort(:,2))));
% Fth1=flip(cumsum(flip(mfsort(:,3))));
% Fth2=flip(cumsum(flip(mfsort(:,4))));
% figure%(1)
% semilogy(mag,Fobs,'r',mag,Fth1,'g',mag,Fth2,'b');
% grid;
% axis([3,8,.0001,100]);
%
%    Evaluate whether total number of quakes is consistent with H1
%
%
Nhat=sum(vLambdaH);
peq=poisspdf(nNumberQuake, Nhat); % probability of exactly Nquake
Ple=poisscdf(nNumberQuake, Nhat); % probability of less than or equal to Nquake
Pless=Ple-peq;              % probability of less than Nquake
Pmore=1-Ple;                 % probability of more than Nquake
rResult.P_H_Equal = peq;
rResult.P_H_Less = Pless;
rResult.P_H_More = Pmore;
rResult.Nhat_H = Nhat;
lamcum1=cumsum(vLambdaH)/rResult.Nhat_H;
%   Evaluate whether total number of quakes is consistent with H2
Nhat=sum(vLambdaN);
peq=poisspdf(nNumberQuake, Nhat); % probability of exactly Nquake
Ple=poisscdf(nNumberQuake, Nhat); % probability of less than or equal to Nquake
Pless=Ple-peq;              % probability of less than Nquake
Pmore=1-Ple;                 % probability of more than Nquake
rResult.P_N_Equal = peq;
rResult.P_N_Less = Pless;
rResult.P_N_More = Pmore;
rResult.Nhat_N = Nhat;
lamcum2=cumsum(vLambdaN)/rResult.Nhat_N;
%
%   simulate catalogs according to H1,
%   and evaluate likelihood scores of nsquake1 and real catalog using lamda1 and lamda2
%
try
  nsquake=simulate(nNumberQuake, vLambdaH, nNumberSimulation);
  [rResult.LLR_H, rResult.fRank11, rResult.fRank12] = Rtest(vLambdaH, vLambdaN, vNumberQuake, nsquake, vWeight);
catch
  rResult.LLR_H = nan;
  rResult.fRank11 = nan;
  rResult.fRank12 = nan;
end
%
%   simulate catalogs according to H2,
%   and evaluate likelihood scores of nsquake1 and real catalog using lamda1 and lamda2
%
try
  nsquake=simulate(nNumberQuake, vLambdaN, nNumberSimulation);
  [rResult.LLR_N, rResult.fRank21, rResult.fRank22] = Rtest(vLambdaH, vLambdaN, vNumberQuake, nsquake, vWeight);
catch
  rResult.LLR_N = nan;
  rResult.fRank21 = nan;
  rResult.fRank22 = nan;
end
%
%Plot cumulative likelihood scores for two hypotheses
%
rResult.fAlpha = sum(rResult.LLR_N > 0)/nNumberSimulation;
rResult.fBeta = sum(rResult.LLR_H < 0)/nNumberSimulation;
%index=[1:nNumberSimulation]/nNumberSimulation;
%x=[0,0];y=[0,1];
%figure_w_normalized_uicontrolunits(2);
%plot(rResult.LLR_H,index,'g',rResult.LLR_N,index,'r',x,y,'b')';
%xlabel('Likelihood ratio (Variable b/Constant b)')';
%ylabel('Fraction of cases');
%title('Green assumes variable-b hypothesis; Red assumes constant=b hypothesis');
% nNumberQuake, Nhat1,Nhat2,P1_less,P1_more,P2_less,P2_more, alpha, beta, rank11,rank12, rank21, rank22
rResult.nNumberQuake = nNumberQuake;
rResult.nNumberSimulation = nNumberSimulation;


function left = r8vec_bracket4 ( nt, t, ns, s )

%*****************************************************************************80
%
%% R8VEC_BRACKET4 finds the interval to each of a vector of values.
%
%  Discussion:
%
%    An R8VEC is a vector of R8's.
%
%    The routine always returns the index LEFT of the sorted array
%    T with the property that either
%    *  T is contained in the interval [ T(LEFT), T(LEFT+1) ], or
%    *  T < T(LEFT) = T(1), or
%    *  T > T(LEFT+1) = T(N).
%
%    The routine is useful for interpolation problems, where
%    the abscissa must be located within an interval of data
%    abscissas for interpolation, or the "nearest" interval
%    to the (extreme) abscissa must be found so that extrapolation
%    can be carried out.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    30 April 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NT, length of the input array.
%
%    Input, real T(NT), an array that has been sorted
%    into ascending order.
%
%    Input, integer NS, the number of points to be bracketed.
%
%    Input, real S(NS), values to be bracketed by entries of T.
%
%    Output, integer LEFT(NS).
%    LEFT(I) is set so that the interval [ T(LEFT(I)), T(LEFT(I)+1) ]
%    is the closest to S(I); it either contains S(I), or else S(I)
%    lies outside the interval [ T(1), T(NT) ].
%

%
%  Check the input data.
%
  if ( nt < 2 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R8VEC_BRACKET4 - Fatal error!\n' );
    fprintf ( 1, '  NT must be at least 2.\n' );
    error ( 'R8VEC_BRACKET4 - Fatal error!' );
  end

  for i = 1 : ns

    left(i) = floor ( ( nt + 1 ) / 2 );
%
%  CASE 1: S < T(LEFT):
%  Search for S in [T(I), T(I+1)] for intervals I = 1 to LEFT-1.
%
    if ( s(i) < t(left(i)) )

      if ( left(i) == 1 )
        continue
      elseif ( left(i) == 2 )
        left(i) = 1;
        continue
      elseif ( t(left(i)-1) <= s(i) )
        left(i) = left(i) - 1;
        continue
      elseif ( s(i) <= t(2) )
        left(i) = 1;
        continue
      end
%
%  ...Binary search for S in [T(I), T(I+1)] for intervals I = 2 to LEFT-2.
%
      low = 2;
      high = left(i) - 2;

      while ( 1 )

        if ( low == high )
          left(i) = low;
          break
        end

        mid = floor ( ( low + high + 1 ) / 2 );

        if ( t(mid) <= s(i) )
          low = mid;
        else
          high = mid - 1;
        end

      end
%
%  CASE2: T(LEFT+1) < S:
%  Search for S in [T(I),T(I+1)] for intervals I = LEFT+1 to N-1.
%
    elseif ( t(left(i)+1) < s(i) )

      if ( left(i) == nt - 1 )
        continue
      elseif ( left(i) == nt - 2 )
        left(i) = left(i) + 1;
        continue
      elseif ( s(i) <= t(left(i)+2) )
        left(i) = left(i) + 1;
        continue
      elseif ( t(nt-1) <= s(i) )
        left(i) = nt - 1;
        continue
      end
%
%  ...Binary search for S in [T(I), T(I+1)] for intervals I = LEFT+2 to NT-2.
%
      low = left(i) + 2;
      high = nt - 2;

      while ( 1 )

        if ( low == high )
          left(i) = low;
          break
        end

        mid = floor ( ( low + high + 1 ) / 2 );

        if ( t(mid) <= s(i) )
          low = mid;
        else
          high = mid - 1;
        end

      end
%
%  CASE3: T(LEFT) <= S <= T(LEFT+1):
%  S is in [T(LEFT), T(LEFT+1)].
%
    else

    end

  end

  return
end


function [B0, beta0, phi0, S0, alpha0]=nwprior(ar,arvar,lambda1,lambda3,lambda4,n,m,p,k,q,prior,priorexo)

% function [B0 beta0 phi0 S0 alpha0]=nwprior(ar,arvar,lambda1,lambda3,lambda4,n,m,p,k,q,prior,bex)
% returns prior values from hyperparameters, for the normal-Wishart prior
% inputs:  - scalar 'ar': prior value of the autoregressive coefficient on own first lag (defined p 15 of technical guide)
%          - vector 'arvar': residual variance of individual AR models estimated for each endogenous variable
%          - scalar 'lambda1': overall tightness hyperparameter (defined p 16 of technical guide)
%          - scalar 'lambda2': cross-variable weighting hyperparameter(defined p 16 of technical guide)
%          - scalar 'lambda3': lag decay hyperparameter (defined p 16 of technical guide)
%          - scalar 'lambda4': exogenous variable tightness hyperparameter (defined p 17 of technical guide)
%          - integer 'n': number of endogenous variables in the BVAR model (defined p 7 of technical guide)
%          - integer 'm': number of exogenous variables in the BVAR model (defined p 7 of technical guide)
%          - integer 'p': number of lags included in the model (defined p 7 of technical guide)
%          - integer 'k': number of coefficients to estimate for each equation in the BVAR model (defined p 7 of technical guide)
%          - integer 'q': total number of coefficients to estimate for the BVAR model (defined p 7 of technical guide)
%          - integer 'prior': value to determine which prior applies to the model
% outputs: - matrix 'B0': the non-vectorised form of beta0
%          - vector 'beta0': vector of prior values for beta (defined in 1.3.4)
%          - matrix 'phi0': prior covariance matrix for the VAR coefficients in the case of a normal-Wishart prior (defined in 1.4.7)
%          - matrix 'S0': prior scale matrix for sigma (defined in 1.4.11)
%          - integer 'alpha0': prior degrees of freedom for sigma (defined in 1.4.11)

% start with beta0, defined in (1.3.4)
beta0=zeros(q,1);

idx = 1:n;
if isscalar(ar)
    beta0((idx-1)*k+idx,1) = ar;
else
    beta0((idx-1)*k+idx,1) = ar(idx,1);
end

% if a prior for the exogenous variables is selected put it in here:
for ii=1:n
    for jj=1:m
        beta0(k*ii-m+jj)=priorexo(ii,jj);
    end
end
% unvectorize (reshape) the vector to obtain the matrix B0
B0=reshape(beta0,k,n);

% next compute phi0, the variance-covariance matrix of beta, defined in (1.4.7)

% set first phi0 as a k*k matrix of zeros
phi0=zeros(k,k);

% set the variance for coefficients on lagged values, using (1.4.5)
for ii=1:n
    for jj=1:p
        phi0((jj-1)*n+ii,(jj-1)*n+ii)=(1/arvar(ii,1))*(lambda1/jj^lambda3)^2;
    end
end

% set the variance for exogenous variables, using (1.4.6)
for ii=1:m
    phi0(k-m+ii,k-m+ii)=(lambda1*lambda4(1,ii))^2;
end

% now compute alpha0 from (1.4.12)
alpha0=n+2;

% and finally compute S0, depending on which choice has been made for the prior ((1.4.13) or identity)
if prior==21
    S0=(alpha0-n-1)*diag(arvar);
elseif prior==22
    S0=eye(n);
else
end


classdef RWMOP17 < PROBLEM
% <multi> <real> <constrained>
% Bulk carriers design problem

%------------------------------- Reference --------------------------------
% A. Kumar, G. Wu, M. Ali, Q. Luo, R. Mallipeddi, P. Suganthan, and S. Das,
% A benchmark-suite of real-world constrained multi-objective optimization
% problems and some baseline results, Swarm and Evolutionary Computation,
% 2021, 67: 100961.
%------------------------------- 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
        %% Initialization
        function Setting(obj)
            obj.M        = 3;
            obj.D        = 6;
            obj.lower    = [150.0 20.0 13.0 10.0 14.0 0.63];
            obj.upper    = [274.32 32.31 25.0 11.71 18.0 0.75];
            obj.encoding = ones(1,obj.D);
        end
        %% Evaluate multiple solutions
        function Population = Evaluation(obj,varargin)
            x   = varargin{1};
            L   = x(:,1);
            B   = x(:,2);
            D   = x(:,3);
            T   = x(:,4);
            V_k = x(:,5);
            C_B = x(:,6);

            a   = 4977.06.*C_B.^2 - 8105.61.*C_B + 4456.51;
            b   = -10847.2.*C_B.^2 + 12817.*C_B - 6960.32;
            F_n = 0.5144./(9.8065 .* L).^0.5;
            P   = ((1.025.*L.*B.*T.*C_B).^(2/3).*V_k.^3)./(a + b.*F_n);

            W_s = 0.034.*L.^1.7.*B.^0.6.*D.^0.4.*C_B.^0.5;
            W_o = L.^0.8.*B.^0.6.*D.^0.3.*C_B.^0.1;
            W_m = 0.17.*P.^0.9;
            ls  = W_s+W_o+W_m;

            D_wt  = 1.025.*L.*B.*T.*C_B-ls;
            F_c   = 0.19.*24.*P./1000 + 0.2;
            D_cwt = D_wt - F_c.*((5000.*V_k)./24 + 5)-2.*D_wt.^0.5;
            R_trp = 350./((5000.*V_k)./24 + 2.*(D_cwt./8000 + 0.5));
            ac    = D_cwt.*R_trp;
            S_d   = 5000.*V_k./24;

            C_c = 0.2.*1.3 .* (2000.*W_s.^0.85 + 3500.*W_o + 2400.*P.^0.8);
            C_r = 40000.*D_wt.^0.3;
            C_v = (1.05.*100.*F_c.*S_d + 6.3.*D_wt.^0.8).*R_trp;

            % Objectives
            f(:,1) = (C_c + C_r + C_v)./ac;
            f(:,2) = ls;
            f(:,3) = -ac;

            % Constraints
            g(:,1) = L./B - 6;
            g(:,2) = 15 - L./D;
            g(:,3) = 19 - L./T;
            g(:,4) = 0.45.*D_wt.^0.31 - T;
            g(:,5) = 0.7.*D + 0.7 - T;
            g(:,6) = 0.32 - F_n;
            g(:,7) = 0.53.*T + ((0.085.*C_B - 0.002).*B.^2)./(T.*C_B)-(1 + 0.52.*D) - 0.07.*B;
            g(:,8) = D_wt - 3000;
            g(:,9) = 500000 - D_wt;
            g      = -g;
            Population = SOLUTION(varargin{1},f,g,varargin{2:end});
            obj.FE     = obj.FE + length(Population);
        end
        %% Generate a point for hypervolume calculation
        function R = GetOptimum(obj,~)
            R = [-3.1514157e+03   8.2606298e+03   8.1260004e+02];
        end
    end
end


function [u,sigma,eqn,info] = PoissonRT0(node,elem,pde,bdFlag,option)
%% POISSONRT0 Poisson equation: lowest order RT element.
%
%  [u,sigma] = PoissonRT0(node,elem,pde,bdFlag) produces an approximation of
%  the Poisson equation
%
%       -div(d*grad(u))=f  in \Omega, with
%       Dirichlet boundary condition u=g_D on \Gamma_D,
%       Neumann boundary condition   d*grad(u)*n=g_N on \Gamma_N
%
%  in the mixed formulation:
%
%  ------------------------------------------------------------------------
%  Find (\sigma , u) in H_{g_N,\Gamma_N}(div,\Omega)\times L^2(\Omega) s.t.
%
%  (d^-1\sigma,\tau) - (div \tau, u)  = <\tau*n,g_D>_{\Gamma_D}
%  \forall \tau in H_{0,\Gamma_N}(div,\Omega)
%    - (div \sigma, v)                =  -(f,v)
%  \forall v in L^2(\Omega)
%
%  where
%  H_{g,\Gamma}(div,\Omega) = {\sigma \in H(div,\Omega); \sigma*n = g
%  on \Gamma \subset \partial\Omega }.
%  ------------------------------------------------------------------------
%
%  The unknown sigma = d*grad(u) is approximated using the lowest order
%  Raviart-Thomas element and u by piecewise constant element (with basis 1).
%
%  [u,sigma] = PoissonRT0(node,elem,pde,bdFlag,option) specifies options
%   - option.solver
%     'direct': the built in direct solver \ (mldivide)
%     'dmg':     multigrid-type solvers mg is used.
%     'uzawapcg': PCG for the Schur complement equation
%     'none': only assemble the matrix equation but not solve
%
%   The default setting is to use the direct solver for small size problems
%   and transforming based multigrid solvers for large size problems.
%
%  Example
%
%    exampleRT0
%
% Created by Ming Wang. Reorganized by Long Chen. Change basis for u.
%
% Copyright (C) Long Chen. See COPYRIGHT.txt for details.

if ~exist('option','var'), option = []; end

%% Diffusion coefficient
if ~isfield(pde,'d'), pde.d = []; end
if isfield(pde,'d') && ~isempty(pde.d)
   if isnumeric(pde.d)
      K = pde.d;                   % d is an array
   else                            % d is a function
      center = (node(elem(:,1),:) + node(elem(:,2),:) + node(elem(:,3),:))/3;
      K = pde.d(center);  % take inverse sequencil.
   end
else
    K = [];
end

%% Data structure
elemold = elem;
[elem,bdFlag] = sortelem(elem,bdFlag);  % ascend ordering
[elem2edge,edge] = dofedge(elem);
NT = size(elem,1); NE = size(edge,1);
[Dlambda,area,elemSign] = gradbasis(node,elem);

%% Assemble matrix
Nsigma = NE; Nu = NT; Ndof = Nsigma + Nu;

% M. Mass matrix for RT0 element
M = getmassmatvec(elem2edge,area,Dlambda,'RT0',K);

% B. negative divergence operator
B = icdmat(double(elem2edge),elemSign*[1 -1 1]);

% C. zero matrix.
C = sparse(Nu,Nu);

A = [M B';B C];

%% Assemble right hand side.
fu = zeros(Nu,1);
if ~isfield(pde,'f') || (isreal(pde.f) && (pde.f==0))
    pde.f = [];
end
if ~isfield(option,'fquadorder')
    option.fquadorder = 2;   % default order
end
if ~isempty(pde.f)
	[lambda,weight] = quadpts(option.fquadorder);
	nQuad = size(lambda,1);
    for p = 1:nQuad
		% quadrature points in the x-y coordinate
		pxy = lambda(p,1)*node(elem(:,1),:) ...
			+ lambda(p,2)*node(elem(:,2),:) ...
			+ lambda(p,3)*node(elem(:,3),:);
		fp = pde.f(pxy);
		fu = fu - fp*weight(p);
    end
    fu = fu.*area;
end
clear fp area
F((Nsigma+1):(Ndof),1) = fu;

%% Boundary Conditions
if ~exist('bdFlag','var'), bdFlag = []; end
[AD,F,bigu,freeDof,isPureNeumannBC] = getbdRT0(F);
eqn = struct('M',AD(1:NE,1:NE),'B',AD(NE+1:end,1:NE),'C',AD(NE+1:end,NE+1:end),...
             'f',F(1:NE),'g',F(NE+1:end),'freeDof',freeDof);

%% Solve the linear system.
% Set up solver type
if isempty(option) || ~isfield(option,'solver')    % no option.solver
    if Ndof <= 2e3  % Direct solver for small size systems
        option.solver = 'direct';
    else         % MGCG  solver for large size systems
        option.solver = 'dmg';
    end
elseif strcmp(option.solver,'mg')
    option.solver = 'dmg';
end
solver = option.solver;
% solve
switch lower(solver);
    case 'direct'
      bigu(freeDof) = AD(freeDof,freeDof)\F(freeDof);
        sigma = bigu(1:NE);
        u = bigu(NE+1:end); info =[];
    case 'none'
        sigma = []; u = []; info =[];
    case 'dmg'
        [sigma,u] = dmg(eqn.M,eqn.B,eqn.C,eqn.f,eqn.g,elemold);
    case 'uzawapcg'
        [sigma,u] = uzawapcg(eqn.M,eqn.B,eqn.C,eqn.f,eqn.g,elemold);
end
info.solverTime = 0; info.assembleTime = 0; info.itStep = 0;
info.stopErr = 0; info.flag = 0;
if isPureNeumannBC == true % post process for u.
    ubar = sum(u.*area)/sum(area);
    u = u - ubar;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function [AD,F,bigu,freeDof,isPureNeumannBC] = getbdRT0(F)
    %% GETBDRT0 Boundary conditions for Poisson equation: RT0 element.
    %
    %  Created by Ming Wang. Improved the check of edgeSign by Long Chen.

    %%
    bigu = zeros(Ndof,1);

    %% Boundary conditions
    if ~isfield(pde,'g_D'), pde.g_D = []; end
    if ~isfield(pde,'g_N'), pde.g_N = []; end

    %% Set up bdFlag
    if isempty(bdFlag) % no bdFlag information
       if ~isempty(pde.g_N) % case: Neumann
           bdFlag = setboundary(node,elem,'Neumann');
       elseif ~isempty(pde.g_D) % case: Dirichlet
           bdFlag = setboundary(node,elem,'Dirichlet');
       end
    end

    %% Find Dirichlet and Neumann dofs
    if ~isempty(bdFlag)
        isDirichlet(elem2edge(bdFlag(:)==1)) = true;
        isNeumann(elem2edge(bdFlag(:)==2)) = true;
        % Direction of boundary edges may not be the outwards normal
        % direction of the domain. edgeSign is introduced to record this
        % inconsistency.
        edgeSign = ones(NE,1);
        idx = (bdFlag(:,1) ~= 0) & (elemSign == -1);% first edge is on boundary
        edgeSign(elem2edge(idx,1)) = -1;
        idx = (bdFlag(:,2) ~= 0) & (elemSign == 1); % second edge is on boundary
        edgeSign(elem2edge(idx,2)) = -1;
        idx = (bdFlag(:,3) ~= 0) & (elemSign == -1);% first edge is on boundary
        edgeSign(elem2edge(idx,3)) = -1;
    end
    Dirichlet = edge(isDirichlet,:);
    Neumann = edge(isNeumann,:);
    isBdDof = false(Ndof,1);
    isBdDof(isNeumann) = true;   % for mixed method, Neumann edges are fixed
    freeDof = find(~isBdDof);

    %% Dirichlet boundary condition (Neumann BC in mixed form)
    %   We need only modify the rhs on dof associated with Dirichlet
    %   boundary. Compute the int_e \Phi\cdot n g_D on the boundary using
    %   quadrature rules.
    if ~isempty(pde.g_D) && isnumeric(pde.g_D) && (pde.g_D==0)
        pde.g_D = [];
    end
    if ~isempty(pde.g_D) && any(isDirichlet)
        if ~isfield(option,'gNquadorder')
            option.gNquadorder = 2;   % default order exact for linear gN
        end
        [lambda,weight] = quadpts1(option.gNquadorder);
        nQuad = size(lambda,1);
        for ip = 1:nQuad
        	pxy = lambda(ip,1)*node(Dirichlet(:,1),:)+...
                  lambda(ip,2)*node(Dirichlet(:,2),:);
            F(isDirichlet) = F(isDirichlet) + weight(ip)*pde.g_D(pxy);
        end
        F(isDirichlet) = F(isDirichlet).*edgeSign(isDirichlet);
        % no edge length since the basis of sigma contains it.
    end

    %% Neumann boundary condition (Dirichlet BC in mixed form)
    if ~isempty(pde.g_N) && any(isNeumann)
        % modify the rhs to include Dirichlet boundary condition
        mid = 1/2*(node(Neumann(:,1),:)+node(Neumann(:,2),:));
        ve = node(Neumann(:,1),:)-node(Neumann(:,2),:);
        edgeLength = sqrt(sum(ve.^2,2));
        if isnumeric(pde.g_N)
            evalg_N = pde.g_N;
        else
            evalg_N = pde.g_N(mid);
        end
        bigu(isNeumann) = edgeLength.*evalg_N;
        if ~isempty(pde.d)
            bigu(isNeumann) = pde.d(mid).*bigu(isNeumann);
        end
        bigu(isNeumann) = bigu(isNeumann).*edgeSign(isNeumann);
        F = F - A*bigu;
        F(isNeumann) = bigu(isNeumann);
    end

    %% Pure Neumann boundary condition
    isPureNeumannBC = false;
    if ~any(isDirichlet) && any(isNeumann)
        freeDof = freeDof(1:end-1);  % eliminate the kernel by enforcing u(NT) = 0;
        isBdDof(end) = true;
        isPureNeumannBC = true;
%         F(end) = 0;
    end

    %% Modify the matrix
    %  Build Neumann boundary condition(Dirichlet BC in mixed form) into the
    %  matrix AD by enforcing  |AD(bdNode,bdNode)=I,
    %  AD(bdNode,FreeNode)=0, AD(FreeNode,bdNode)=0|.
    if any(isBdDof)
       bdidx = zeros(Ndof,1);
       bdidx(isBdDof) = 1;
       Tbd = spdiags(bdidx,0,Ndof,Ndof);
       T = spdiags(1-bdidx,0,Ndof,Ndof);
       AD = T*A*T + Tbd;
    else
       AD = A;
    end
    end
end


function L=ishappy(n)

N=n;
L=0;
NHistory=n;
while L==0
    N=sum(str2num(num2str(N).').^2); %Sum digits
    if N==1 %n is a happy number
        L=1;
    elseif ismember(N,NHistory); %Kill we are in a loop
        break
    end
    NHistory=[NHistory N];
end
NHistory=[NHistory N];

%%
% _*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 [coeff, score, latent, tsquare] = princomp_largedata(x,econFlag)
% Principal Components Analysis
%
% This is a version created for large data sets by Matthew Davidson
%
% The default Matlab PRINCOMP is naive to large data sets. Out-of-memory
% errors are easily obtained on imaging data. The solution is to replace
% concise but inefficient calls to repmat with loops and to eliminate
% large, unused variables.
%
% Tested on random data sets and produces identical output as original
% PRINCOMP. Speed penalty is drastic for small data. 50% slower on
% 50x1000 element data set, but on a 50x10000, only 2% slower.
%
% COEFF = PRINCOMP(X) performs principal components analysis on the N-by-P
% data matrix X, and returns the principal component coefficients, also
% known as loadings.  Rows of X correspond to observations, columns to
% variables.  COEFF is a P-by-P matrix, each column containing coefficients
% for one principal component.  The columns are in order of decreasing
% component variance.
%
% PRINCOMP centers X by subtracting off column means, but does not
% rescale the columns of X.  To perform PCA with standardized variables,
% i.e., based on correlations, use PRINCOMP(ZSCORE(X)).  To perform PCA
% directly on a covariance or correlation matrix, use PCACOV.
%
% [COEFF, SCORE] = PRINCOMP(X) returns the principal component scores,
% i.e., the representation of X in the principal component space.  Rows
% of SCORE correspond to observations, columns to components.
%
% [COEFF, SCORE, LATENT] = PRINCOMP(X) returns the principal component
% variances, i.e., the eigenvalues of the covariance matrix of X, in
% LATENT.
%
% [COEFF, SCORE, LATENT, TSQUARED] = PRINCOMP(X) returns Hotelling's
% T-squared statistic for each observation in X.
%
% When N <= P, SCORE(:,N:P) and LATENT(N:P) are necessarily zero, and the
% columns of COEFF(:,N:P) define directions that are orthogonal to X.
%
% [...] = PRINCOMP(X,'econ') returns only the elements of LATENT that are
% not necessarily zero, i.e., when N <= P, only the first N-1, and the
% corresponding columns of COEFF and SCORE.  This can be significantly
% faster when P >> N.
%
% :See Also: BARTTEST, BIPLOT, CANONCORR, FACTORAN, PCACOV, PCARES, ROTATEFACTORS.
%
% :References:
%   1. Jackson, J.E., A User's Guide to Principal Components,
%      Wiley, 1988.
%   2. Jolliffe, I.T. Principal Component Analysis, 2nd ed.,
%      Springer, 2002.
%   3. Krzanowski, W.J., Principles of Multivariate Analysis,
%      Oxford University Press, 1988.
%   4. Seber, G.A.F., Multivariate Observations, Wiley, 1984.
%
% ..
%   Copyright 1993-2005 The MathWorks, Inc.
%   $Revision: 2.9.2.9 $  $Date: 2006/10/02 16:35:01 $
% ..

% ..
%    When X has more variables than observations, the default behavior is to
%    return all the pc's, even those that have zero variance.  When econFlag
%    is 'econ', those will not be returned.
% ..

if nargin < 2, econFlag = 0; end

[n,p] = size(x);
if isempty(x)
    pOrZero = ~isequal(econFlag, 'econ') * p;
    coeff = zeros(p,pOrZero); coeff(1:p+1:end) = 1;
    score = zeros(n,pOrZero);
    latent = zeros(pOrZero,1);
    tsquare = zeros(n,1);
    return
end

% Center X by subtracting off column means
%x0 = x - repmat(mean(x,1),n,1);
for i=1:size(x, 2)
    x(:,i) = x(:,i) - mean(x(:,i));
end
r = min(n-1,p); % max possible rank of X0

% The principal component coefficients are the eigenvectors of
% S = X0'*X0./(n-1), but computed using SVD.
[U,sigma,coeff] = svd(x,econFlag); % put in 1/sqrt(n-1) later

if nargout < 2
    % When econFlag is 'econ', only (n-1) components should be returned.
    % See comment below.
    if (n <= p) && isequal(econFlag, 'econ')
        coeff(:,n) = [];
    end

else
    % Project X0 onto the principal component axes to get the scores.
    if n == 1 % sigma might have only 1 row
        sigma = sigma(1);
    else
        sigma = diag(sigma);
    end
    score = U .* repmat(sigma',n,1); % == x*coeff
    sigma = sigma ./ sqrt(n-1);

    % When X has at least as many variables as observations, eigenvalues
    % n:p of S are exactly zero.
    if n <= p
        % When econFlag is 'econ', nothing corresponding to the zero
        % eigenvalues should be returned.  svd(,'econ') won't have
        % returned anything corresponding to components (n+1):p, so we
        % just have to cut off the n-th component.
        if isequal(econFlag, 'econ')
            sigma(n,:) = []; % make sure this shrinks as a column
            coeff(:,n) = [];
            score(:,n) = [];

        % Otherwise, set those eigenvalues and the corresponding scores to
        % exactly zero.  svd(,0) won't have returned columns of U
        % corresponding to components (n+1):p, need to fill those out.
        else
            sigma(n:p,1) = 0; % make sure this extends as a column
            score(:,n:p) = 0;
        end
    end

    % The variances of the pc's are the eigenvalues of S = X0'*X0./(n-1).
    latent = sigma.^2;

    % Hotelling's T-squared statistic is the sum of squares of the
    % standardized scores, i.e., Mahalanobis distances.  When X appears to
    % have column rank < r, ignore components that are orthogonal to the
    % data.
    if nargout == 4
        if n > 1
            q = sum(sigma > max(n,p).*eps(sigma(1)));
            if q < r
                warning('stats:princomp:colRankDefX', ...
                        ['Columns of X are linearly dependent to within machine precision.\n' ...
                         'Using only the first %d components to compute TSQUARED.'],q);
            end
        else
            q = 0;
        end
        tsquare = (n-1) .* sum(U(:,1:q).^2,2); % == sum((score*diag(1./sigma)).^2,2)
    end
end


function data = get_kernel_data(X,kernel, kernel_params, Xtrain, datatrain);
% nargin >= 4, when computations of the testing data depends on the training data (i.e., with incomplete Cholesky)

switch kernel
	case { 'polynomial','polynomial-mkl','polynomial-bimkl'}
		[n , p ] = size(X);
		q = kernel_params(1);
		data.n = n ;
		data.p = p;
		data.q = q;
		data.qs = (q+1) * ones(1,p);		  % number of kernels per directions
		data.Xs = zeros(n,sum(data.qs));      % storing all dimensions in the same vector
		data.ind_Xs = cell(1,p);
		data.d_Xs = cell(1,p);
		iK = 1;
		for i=1:p
			data.ind_Xs{i} = cell(1,data.qs(i));
			data.d_Xs{i} = zeros(1,data.qs(i));
			jK = iK;
			for j=1:data.qs(i)
				data.ind_Xs{i}{j} = iK;
				data.d_Xs{i}(j) = 1;
				if j==1
					data.Xs(:,iK) = ones(n,1);		% constant term
				else
					data.Xs(:,iK) = X(:,i).^(j-1) * sqrt( 1 / factorial(j-1) * factorial(q) / factorial(q-j+1) );
				end
				iK = iK + 1 ;
			end
			jK = jK + data.qs(i);
		end

	case {'hermite', 'hermite-mkl', 'hermite-bimkl'}
		[n , p ] = size(X);
		q = kernel_params(2);
		alpha = kernel_params(1);
		data.n = n ;
		data.p = p;
		data.q = q;
		data.qs = (q+1) * ones(1,p);		  % number of kernels per directions
		data.Xs = zeros(n,sum(data.qs));      % storing all dimensions in the same vector
		data.ind_Xs = cell(1,p);
		data.d_Xs = cell(1,p);
		iK = 1;
		for i=1:p
			data.ind_Xs{i} = cell(1,data.qs(i));
			data.d_Xs{i} = zeros(1,data.qs(i));
			jK = iK;
			for j=1:data.qs(i)
				data.ind_Xs{i}{j} = iK;
				data.d_Xs{i}(j) = 1;

				if j==1
					data.Xs(:,iK) = ones(n,1);
				else
					data.Xs(:,iK) = hermite_polynomials(j,X(:,i)) * sqrt( (alpha/2)^(j-1) / factorial(j-1) );
				end
				iK = iK + 1 ;
			end
			jK = jK + data.qs(i);
		end

	case {'gauss-hermite', 'gauss-hermite-mkl', 'gauss-hermite-bimkl'}
		[n , p ] = size(X);
		q = kernel_params(3);
		sigma = kernel_params(1);
		b = kernel_params(2);
		a = 1/4/sigma;
		c = sqrt(a*a+2*a*b);
		A = a + b + c;
		data.n = n ;
		data.p = p;
		data.q = q;
		data.qs = (q+1) * ones(1,p);		  % number of kernels per directions
		data.Xs = zeros(n,sum(data.qs));      % storing all dimensions in the same vector
		data.ind_Xs = cell(1,p);
		data.d_Xs = cell(1,p);
		iK = 1;
		for i=1:p
			data.ind_Xs{i} = cell(1,data.qs(i));
			data.d_Xs{i} = zeros(1,data.qs(i));
			jK = iK;
			for j=1:data.qs(i)
				data.ind_Xs{i}{j} = iK;
				data.d_Xs{i}(j) = 1;

				data.Xs(:,iK) =  ( 1 - (b/A)^2).^.25 * hermite_polynomials(j,X(:,i) * sqrt( 2 * c ) ) .* exp( - ( b/A*(a+c) * X(:,i).^ 2 ) ) ...
					* sqrt( (b/A)^(j-1) / ( 2^(j-1) * factorial(j-1) )) ;
				iK = iK + 1 ;
			end
			jK = jK + data.qs(i);
		end

	case {'gauss-hermite-full', 'gauss-hermite-full-mkl', 'gauss-hermite-full-bimkl'}
		% simply the same as 'gauss-hermite', but with the full kernels at
		% the end
		[n , p ] = size(X);
		q = kernel_params(3);
		sigma = kernel_params(1);
		b = kernel_params(2);
		a = 1/4/sigma;
		c = sqrt(a*a+2*a*b);
		A = a + b + c;
		data.n = n ;
		data.p = p;
		data.q = q+1;
		data.qs = (q+2) * ones(1,p);     % number of kernels per directions
		data.Xs = zeros(n,sum(data.qs));      % storing all dimensions in the same vector
		data.ind_Xs = cell(1,p);
		data.d_Xs = cell(1,p);
		iK = 1;
		for i=1:p
			data.ind_Xs{i} = cell(1,data.qs(i));
			data.d_Xs{i} = zeros(1,data.qs(i));
			jK = iK;
			fulldataloc = [];
			for j=1:data.qs(i)-1
				% first kernels as before
				data.ind_Xs{i}{j} = iK;
				data.d_Xs{i}(j) = 1;

				data.Xs(:,iK) =  ( 1 - (b/A)^2).^.25 * hermite_polynomials(j,X(:,i) * sqrt( 2 * c ) ) .* exp( - ( b/A*(a+c) * X(:,i).^ 2 ) ) ...
					* sqrt( (b/A)^(j-1) / ( 2^(j-1) * factorial(j-1) )) ;
				fulldataloc = [ fulldataloc; iK ];

				iK = iK + 1 ;
			end

			if nargin < 4
				% last kernel: first build the kernel matrix, then takes its
				% incomplete Cholesky decomposition

				K = exp( - b * sqdist(X(:,i)',X(:,i)' ) ) - data.Xs(:,fulldataloc)  * data.Xs(:,fulldataloc)';
				[Gf,Pf,mf,residualf] = icd_general(K,1e-3,kernel_params(6));
				If = Pf(1:mf);
				data.Is{i} = If;					% these two for test data

				[temp,Pif] = sort(Pf);
				Gf = Gf(Pif,1:mf);
				data.d_Xs{i}(end) = mf;
				data.ind_Xs{i}{end} = iK:iK+mf-1;
				data.Xs(:,data.ind_Xs{i}{end}) = Gf;
				iK = iK+mf;
			else
				% last kernel for test data! This compute testing data
				K = exp( - b * sqdist(X(:,i)',Xtrain(datatrain.Is{i},i)' ) ) - data.Xs(:,fulldataloc)  * datatrain.Xs(datatrain.Is{i},fulldataloc)';
				data.d_Xs{i}(end) = datatrain.d_Xs{i}(end);
				data.ind_Xs{i}{end} = datatrain.ind_Xs{i}{end};
				data.Xs(:,data.ind_Xs{i}{end}) = K * inv( datatrain.Xs(datatrain.Is{i},data.ind_Xs{i}{end})' ) ;
				iK = iK+data.d_Xs{i}(end);

			end
		end

	case {'anova', 'anova-mkl', 'anova-bimkl'}
		% simply the same as 'gauss-hermite', but with the full kernels at
		% the end
		[n , p ] = size(X);
		b = kernel_params(1);

		data.n = n ;
		data.p = p;
		data.q = 1;
		data.qs = 2 * ones(1,p);     % number of kernels per directions
		data.Xs = zeros(n,sum(data.qs));      % storing all dimensions in the same vector
		data.ind_Xs = cell(1,p);
		data.d_Xs = cell(1,p);
		iK = 1;
		for i=1:p
			% constant term
			data.ind_Xs{i} = cell(1,2);
			data.d_Xs{i} = zeros(1,2);
			data.ind_Xs{i}{1} = iK;
			data.d_Xs{i}(1) = 1;
			data.Xs(:,iK) = ones(size(X,1),1);
			iK = iK + 1 ;

			if nargin < 4
				% first build the kernel matrix, then takes it
				% incomplete Cholesky decomposition

				K = exp( - b * sqdist(X(:,i)',X(:,i)' ) );
				[Gf,Pf,mf,residualf] = icd_general(K,1e-3,kernel_params(4));
				If = Pf(1:mf);

				data.Is{i} = If;					% these two for test data
				[temp,Pif] = sort(Pf);
				Gf = Gf(Pif,1:mf);
				data.d_Xs{i}(2) = mf;
				data.ind_Xs{i}{2} = iK:iK+mf-1;
				data.Xs(:,data.ind_Xs{i}{2}) = Gf;
				iK = iK+mf;
			else
				% last kernel for test!
				K = exp( - b * sqdist(X(:,i)',Xtrain(datatrain.Is{i},i)' ) );
				data.d_Xs{i}(2) = datatrain.d_Xs{i}(2);
				data.ind_Xs{i}{2} = datatrain.ind_Xs{i}{2};
				data.Xs(:,data.ind_Xs{i}{2}) = K * inv( datatrain.Xs(datatrain.Is{i},data.ind_Xs{i}{2})' ) ;
				iK = iK+data.d_Xs{i}(2);

			end
		end

	case {'base kernels', 'base kernels-mkl', 'base kernels-bimkl'}
		p = kernel_params(1);
		if nargin<4
			% train
			n = size(X,1);
			n = floor( sqrt(2 * n) );
		else
			% test
			ntrain = size(Xtrain,1);
			ntrain = floor( sqrt(2 * ntrain) );
			n = size(X,1) / ntrain;
		end
		q = kernel_params(2);
		data.n = n ;
		data.p = p;
		data.q = kernel_params(2);
		data.qs = (q+1) * ones(1,p);			% number of kernels per directions
		data.Xs = zeros(n,sum(data.qs));		% storing all dimensions in the same vector
		data.ind_Xs = cell(1,p);
		data.d_Xs = cell(1,p);
		iK = 1;
		for i=1:p
			% constant term
			data.ind_Xs{i} = cell(1,2);
			data.d_Xs{i} = zeros(1,2);
			data.ind_Xs{i}{1} = iK;
			data.d_Xs{i}(1) = 1;
			data.Xs(:,iK) = ones(n,1);
			iK = iK + 1 ;

			if nargin < 4
				% first build the kernel matrix, then takes it
				% incomplete Cholesky decomposition
				for j=1:q
					K = double( devectorize_single(X(:,i,j)) );

					[Gf,Pf,mf,residualf] = icd_general(K,1e-3,kernel_params(5));
					If = Pf(1:mf);

					data.Is{i}{j+1} = If;					% these two for test data
					[temp,Pif] = sort(Pf);
					Gf = Gf(Pif,1:mf);
					data.d_Xs{i}(j+1) = mf;
					data.ind_Xs{i}{j+1} = iK:iK+mf-1;

					data.Xs(:,data.ind_Xs{i}{j+1}) = Gf;
					iK = iK+mf;
				end
			else
				for j=1:q

					K = double( reshape(X(:,i,j) ,n,ntrain ) );
					K = K(:,datatrain.Is{i}{j+1});
					data.d_Xs{i}(j+1) = datatrain.d_Xs{i}(j+1);
					data.ind_Xs{i}{j+1} = datatrain.ind_Xs{i}{j+1};
					data.Xs(:,data.ind_Xs{i}{j+1}) = K * inv( datatrain.Xs(datatrain.Is{i}{j+1},data.ind_Xs{i}{j+1})' ) ;
					iK = iK+data.d_Xs{i}(j+1);

				end
			end
		end

	case {'spline', 'spline-mkl', 'spline-bimkl'}
		% simply the same as 'gauss-hermite', but with the full kernels at
		% the end
		[n , p ] = size(X);
		data.n = n ;
		data.p = p;
		data.q = 2;
		data.qs = 3 * ones(1,p);     % number of kernels per directions
		data.Xs = zeros(n,sum(data.qs)*30);      % storing all dimensions in the same vector
		data.ind_Xs = cell(1,p);
		data.d_Xs = cell(1,p);
		iK = 1;
		for i=1:p
			% constant term
			data.ind_Xs{i} = cell(1,2);
			data.d_Xs{i} = zeros(1,2);

			data.ind_Xs{i}{1} = iK;
			data.d_Xs{i}(1) = 1;
			data.Xs(:,iK) = ones(size(X,1),1);
			iK = iK + 1 ;

			data.ind_Xs{i}{2} = iK;
			data.d_Xs{i}(2) = 1;
			data.Xs(:,iK) = X(:,i);
			iK = iK + 1 ;

			if nargin < 4
				% first build the kernel matrix, then takes it
				% incomplete Cholesky decomposition
				K = compute_cubic_spline_kernel(X(:,i),X(:,i));
				[Gf,Pf,mf,residualf] = icd_general(K,1e-3,kernel_params(3));
				If = Pf(1:mf);

				data.Is{i} = If;					% these two for test data
				[temp,Pif] = sort(Pf);
				Gf = Gf(Pif,1:mf);
				data.d_Xs{i}(3) = mf;
				data.ind_Xs{i}{3} = iK:iK+mf-1;
				data.Xs(:,data.ind_Xs{i}{3}) = Gf;
				iK = iK+mf;
			else
				% last kernel for test!
				K = compute_cubic_spline_kernel(X(:,i),Xtrain(datatrain.Is{i},i));
				data.d_Xs{i}(3) = datatrain.d_Xs{i}(3);
				data.ind_Xs{i}{3} = datatrain.ind_Xs{i}{3};
				data.Xs(:,data.ind_Xs{i}{3}) = K * inv( datatrain.Xs(datatrain.Is{i},data.ind_Xs{i}{3})' ) ;
				iK = iK+data.d_Xs{i}(3);

			end
		end

		data.Xs(:,iK:end) = [];

end


 function [yik, scalefactor] = ir_mri_field_map_reg_scale(yik, etime, varargin)
%function [yik, scalefactor] = ir_mri_field_map_reg_scale(yik, etime, varargin)
%|
%| Scale images to account for R2* effects and differences in absolute value
%| using median(ri), where ri = sum_j sum_k |yik_j yik_k|^2 (t_k - t_j)^2
%|
%| in
%|	yik	[N nset]	scan images
%|	etime	[1 nset]	echo times (units of sec if fieldmap is in Hz)
%|
%| option
%|	fmax			threshold for absolute yik value (default 0.1)
%|	dmax			threshold for absolute rj value (default 0.1)
%|	show	0|1		1 to show result (default 0)
%|
%| out
%|	yik	[N nset]	scaled scan images
%|	scalefactor		sqrt(median(rj)) * effect_of_fmax
%|
%| MJ Allison
%| 2015-06-27 JF cosmetic changes

if nargin < 4, ir_usage, end

arg.fmax = 0.1;
arg.dmax = 0.1;
arg.show = 0;
arg = vararg_pair(arg, varargin);

dim_yik = size(yik);
yik = reshapee(yik, [], dim_yik(end)); % [*N nset]

[nn, nset] = size(yik);

% Scale by median of first set of data to get rid of large mag_j
% effects (not actually needed, but left in for consistency)
if arg.fmax > 0
	y1 = abs(yik(:,1));
	scalefactor = median(y1(y1(:) > arg.fmax * max(y1(:))));
	if scalefactor == 0
		fail 'median is zero?'
	end
	yik = yik / scalefactor;
else
	scalefactor = 1;
end

% Try to compensate for R2 effects on effective regularization.

d = zeros(nn,1);
for j = 1:nset
	for k = 1:nset
		tmp = abs(yik(:,j) .* yik(:,k)).^2 * (etime(k)-etime(j)).^2;
		d = d + tmp;
	end
end

% divide by numerator of wj^mn -> sum(abs(y)^2)
d = div0(d, sum(abs(yik).^2,2));

if arg.show
	im(reshape(d, [dim_yik(1:2)]))
end

% compute dtyp
dtyp = median(d(d(:) > arg.dmax * max(d(:))));
% pr dtyp

% now uniformly scale by the square root of dtyp
yik = yik / sqrt(dtyp);

scalefactor = scalefactor * sqrt(dtyp); % 2022-11-27 fix
yik = reshape(yik, dim_yik); % [(N) nset]