%% NUMERICAL LINEAR ALGEBRA WITH MATLAB
% This document looks at some important concepts in linear and shows how to
% solve some basic problems using MATLAB.

%% Matrix-Vector products
% Let's think about what a matrix-vector product actually does. It's more
% interesting that you think! Lets take a simple example. Consider the
% vector [1;0] multiplied by a matrix [1,1;1,1]
%%
figure('color',[1 1 1])
A=ones(2)
x=[1;0]
Ax=A*x
line([0 x(1)],[0 x(2)]), hold on, grid on
line([0,Ax(1)],[0,Ax(2)],'color',[1 0 0])
text(1.1,0,'x','fontweight','bold'), text(1.1,1.1,'A*x','fontweight','bold')
axis([-0.5 1.5 -0.5 1.5])
title('Action of a matrix A on a vector x')
%%
% We can see that the matrix multiplication resulted in a vector that was
% rotated by a certain angle and also stretched by a certain factor. This
% action (stretching and rotating) is extremely useful for switching from
% one co-ordinate system [x,y] to another [x', y']. Something that you will
% come across in special relativity and a number of other areas. To
% visualise what we mean by a co-ordinate transformation, consider the
% basis vectors x:=[1,0] and y:=[0,1], and a point p:=[0.2,0.1].
figure('color',[1 1 1])
xy=[1,0;0,1]
p=[0.5,0.3,0.1;0.4,0.2,0.0]
A=[-1 1;1 1]
xyp=A*xy; % Co-ordinate rotation step
Ap=A*p;
line([0 xy(1,1)],[0 xy(2,1)]), grid on, hold on
line([0,xy(1,2)],[0,xy(2,2)])
plot(p(1,:),p(2,:),'*','markersize',6,'color','b')
line([0, xyp(1,1)],[0 xyp(2,1)],'color',[1 0 0])
line([0, xyp(2,1)],[0,xyp(2,2)],'color',[1 0 0])
plot(Ap(1,:),Ap(2,:),'*','markersize',6,'color','r')
axis([-1.5 1.5 -1.5 1.5]), axis square
title('Changing the co-ordinate basis with matrix multiplication')

%% A rogue's gallery of useful matrices
% Matrices can be thought of as a 2-D array of data, or a collection of
% vectors. There are a number of important matrices that are extremely
% useful, and worth knowing about. We will use several of these later on in
% this lecture. Here are a few:
%%
% * Identity
% * Ones/Zeros
% * Upper/Lower Triangular
% * Symmetric
% * Vandermonde
% * Finite Difference
% * Rotation
%%
% The *Identity Matrix* or *I*
%%
I = eye(4)
%%
% A *matrix of ones or zeros*:
%%
O = ones(4)
Z = zeros(4)
%%
% *Upper and lower triangular matrices*. These are particularly important,
% as we shall see below, and consist of matrices that have zeros below, or
% above the _main diagonal_
%%
UT = triu(randn(4))
LT = tril(randn(4))
%%
% *Symmetric matrices* are matrices that have the same
% elements above he main diagonal as below the main diagonal:
%%
A = randn(4);
SYM = A*A'
%%
% The *Vandermonde matrix* is a matrix whose columns are polynomials in
% $$x$. This is useful in curve fitting and interpolation, for example, if
% I wanted to find the $$3^{rd}$ order interpolating polynomial for some
% data [x,y], then I would use the following matrix:
figure('color',[1 1 1])
x=linspace(0,1,5)';
IM = [x.^0, x.^1, x.^2, x.^3, x.^4]
plot(x,IM), axis([0 1 0 1.2]), grid on
title('First five powers of x')
%%
% Generally for interpolation, it's better to use a matrix whose columns
% are *orthogonal* polynomials, such as Legendre or Chebyshev, but this is
% beyond the remit of this course.
%%
% *Finite difference matrices* are extremely useful. They are matrices that
% approximate the derivative of a function on a grid. One way to calculate
% them is to fit an interpolating polynomial through the function, and then
% differentiate the polynomial to get what is called a finite difference
% stencil, while the other is to use a Taylor expansion to approximate
% the derivatives.
%%
% As an illustration, we will use the Taylor expansion.
%%
% <latex> {\Large
% $ f(x+\Delta x) = f(x) + \Delta x f'(x) + \frac{{\Delta x}^2f''(x)}{2!} +
% $}{\emph higher order terms} </latex>
%%
% Here the dash represents a derivative with respect to x. If we ignore
% terms above the first derivative, we see we can say that
%%
% <latex> {\Large
% $ f'(x) \approx \frac{f(x+ \Delta x) - f(x)}{\Delta x}$} </latex>
%%
% This is called the *forward difference* approximation. If we apply this
% formula to itself, we come up with an approximation for the second
% derivative of a function:
%%
% <latex>{\Large
% $ f''(x) \approx \frac{1}{\Delta x}\left(\frac{f(x+\Delta x+\Delta
% x)-f(x+\Delta x)-f(x+\Delta x)+f(x)}{\Delta x}\right)$}</latex>
%%
% or that, finally:
%%
% <latex> {\Large
% $ f''(x) \approx \frac{1}{\Delta x^2}\left(f(x+2\Delta x
% )-2f(x+\Delta x)+f(x)\right)$}</latex>
%%
% How to turn this into a matrix, however? If we imagine a function
% expressed on a grid of points $${x_1,x_2,\cdots,x_N}$ and replace the
% function evaluation $$f(x+\Delta x)$ with function evaluations at the
% grid points, then the second derivative can be expressed as the following
% matrix:
%%
N=5;
I=ones(N,1);
D2 = spdiags([I, -2*I, I],[-1:1],N,N);
full(D2)
%%
% Here we use the construct
%  spdiags
% to tell MATLAB that the matrix is sparse, i.e. is composed of mainly zero
% elements. In fact, this particular type of sparsity pattern is called
% *tridiagonal*. Consider the second row of this matrix multiplied by a
% vector which is the function evaluated on the grid of points $$x_i$,
% $$\left[f(x_0), f(x_1), f(x_2),\cdots,f(x_N)\right]^T$. We have the following
% result:
%%
% <latex>
% $ \left(\begin{array}{cccccc} 1&-2&1&0&\cdots&0 \end{array}\right)\times
% \left(\begin{array}{c} f(x_0) \\ f(x_1) \\ f(x_2) \\ \vdots \\
% f(x_N)\end{array}\right) = f(x_0)-2\times f(x_1)+f(x_2)$
% </latex>
%%
% That is, the result of multiplying the second row of the differentiation
% matrix by a vector representing our function on the grid, gives an
% approximation to the derivative of our function on the grid. As an
% example, let's calculate the second derivative of $$ f(x)=\exp^{\sin(x)}$
% on a grid of 21 points:
N=21; x=linspace(0,2*pi,N)'; % set up the grid
I=ones(N,1); dx=x(2)-x(1);
D2 = spdiags([I, -2*I, I],[-1:1],N,N)/dx^2; %2nd derivative matrix
f=@(x)(exp(sin(x))) % function
d2 = D2*f(x);       % numerical derivative
d2(1)=1; d2(N)=1;   % fix boundary points
figure('color',[1 1 1])
plot(x,f(x),x,d2), grid on, hold on
d2f=@(x)(cos(x).^2.*exp(sin(x))-sin(x).*exp(sin(x))) % actual second derivative
plot(x,d2f(x),'r-.')
title('Approximating a second derivative using a differentiation matrix');
legend('f(x)=exp^{sin(x)}','Approximate 2^{nd} deriv','Actual 2^{nd} deriv');
hold off
%% Systems of linear equations
% At its heart, the subject of linear algebra is concerned with how to
% solve the seemingly simple equation:
%
% $${\bf{A}}x=b$,
% where $${\bf{A}}$ is a matrix and $$x$ and $$b$ are vectors.
%%
%
% This might seem surprising, but this problem is right at the heart of
% almost all problems in scientific computing, from differential equations
% to eigenvalue problems to interpolation and curve fitting.

%%
% Of course, for a single scalar variable, x, we know what that means:
% $$ a\times x = y$
% from which we can deduce that
% $$ x = y/a$, for non-zero $$a$.
%%
% The question is how do we solve such a problem when we have more than one
% unknown variable, or, equivalently, when x is a vector? To investigate this
% question, let's consider the following electrical circuit:
%
[c,m]=imread('circuit2.gif','gif');

figure('color',[1 1 1]),
image(c), colormap(m), axis equal, axis off

%%
% We want to calculate the currents $$I_1,I_2,I_3$
%%
% Using Kirchoff's voltage laws we get the following three equations for
% the three unknown currents:
%%
% <latex>
% $\left\{\begin{array}{ccc}
% I_1 + 25(I_1-I_2)+50(I_1-I_3) & = & 10 \\
% 25(I_2-I_1) + 30I_2 + I_2 -I_3 & = & 0 \\
% 50(I_3-I_1)+I_3-I_2+55I_3 & = & 0 \end{array}\right . $
% </latex>
%%
% which, with a little re-arranging becomes:
%%
% <latex>
% $\left\{\begin{array}{ccc}
% 76I_1 - 25I_2-50I_3 & = & 10 \\
% -25I_1 +56I_2 -I_3 & = & 0 \\
% -50I_1-I_2+106I_3 & = & 0 \end{array}\right .$
% </latex>
%%
% Finally, we can gather all the coefficients into a matrix, and the
% unknowns into a vector, to write the single matrix linear equation:
%%
% <latex>
% $\left( {\begin{array}{ccc}
% 76 & -25 & -50 \\
% -25 & 56 & -1  \\
% -50& -1 & 106 \end{array}} \right)
% \times \left(\begin{array}{c}
% I_1\\
% I_2\\
% I_3 \end{array}\right) = \left( \begin{array}{c}
% 10 \\
% 0\\
% 0 \end{array}\right)$
% </latex>
%%
% In this way we have got the system of equations into the form that we
% described above, namely:
% $${\bf{A}}x=b$
%%
% Of course, for a small system (3 by 3 in this case) we can solve this by
% hand. For a larger system, however, we need a computer. The basic
% approach to solving such a system is an extension of that used by hand,
% namely we add and subtract multiples of one row to one another until we
% finally end up with an upper triangular matrix. This action
% of adding and subtrating multiples of one row to another is known as a
% basic row operation. Let us illustrate the process with MATLAB. Before we
% begin, we augment the right hand side vector to be an extra column in our
% matrix $$\bf A$, so it is now three rows by four columns.
A=[76 -25 -50 10;-25 56 -1 0; -50 -1 106 0]
rrefexample(A)
%%
% What is the advantage of reducing $${\bf A}$ to be upper triangular?
% Starting from the last row, we see that we now have an equation that
% depends only on $$I_3$, that is $$I_3 = 0.117$. The row above depends on
% $$I_3$ and $$I_2$ only, and we now know what $$I_3$ is! In this way, we
% work our way up through the rows substituting as we go, and in each row
% there is only a single unknown variable. This process is called _back
% substitution_. This entire process - that of reducing the matrix to a
% triangular form and solving via back substitution - is the way most
% computer packages solve these linear systems. In MATLAB, the shorthand
% way of solving such systems is with the backslash character ``\''
A=[76 -25 -50;-25 56 -1; -50 -1 106]
b =[10;0;0]
I= A\b
%%
% In the example above, we have performed a special case of what is a more
% general idea - that of transforming a matrix into a special form, so that
% the equations can be solved more easily. In general, the idea of
% transforming a matrix through a series of elementary row operations is a
% powerful one. May special types of _matrix factorisations_ exist, and two
% of the most important examples are:
%%
% *QR decomposition*
% In this case, a general matrix is written as the product of two matrices,
% one orthogonal and one upper triangular:
% $${\bf A = QR}$
% As the inverse of an orthogonal matrix is equal to its transpose, we find
% the solution to the general system $${\bf A}x=b$ can be calculated as:
%%
% <latex>
% $\begin{array}{l}
% {\bf A}x = b \\
% {\bf QR}x = b \\
% {\bf Q}({\bf R}x)=b \\
% {\bf R}x = {\bf Q^T} \times b \\
% x = {\bf R}\setminus{\bf Q^T} \times b \end{array}$
% </latex>
%%
% *LU Decomposition*
% In this case the matrix is decomposed into two, a lower and an upper
% triangular matrix. As both the matrices are triangular, the back (and
% forward) substitution process is very quick:
%%
% <latex>
% $\begin{array}{l}
% {\bf A}x = b \\
% {\bf LU}x = b \\
% {\bf L}({\bf U}x)=b \\
% {\bf L}x = {\bf U}\setminus b \\
% x = {\bf L}\setminus({\bf U}\setminus b) \end{array}$
% </latex>
%%
% As another example of using MATLAB to solve linear systems, let us
% perform a least squares fit on some experimental data. We have run an
% experiment to measure the distance fallen by an object in a given time.
% We know from the laws of motion that the distance is related to the
% initial velocity and position, and the acceleration through a quadratic
% relationship:
%%
% $$x(t) = x(0) + v(0)t + \frac{1}{2}gt^2$
%%
% We want to fit a quadratic line of best fit through the data, and hence
% work out a rough estimate for $$g$ the acceleration due to gravity.
load fall_data
t=data(:,1);
dist=data(:,2);
I=ones(size(t));
R=[I,t,t.^2];  % This is a VANDERMONDE matrix (see above)
coeffs = R\dist
g=2*coeffs(3)  % approximation for g (coeff is g/2)
plot(t,dist,'o',t,R*coeffs,'r'), grid on
title('Fitting a quadratic curve to experimental data')

%% Eigenvectors and Eigenvalues of a matrix
% As we have seen, the action of a mtrix on a vector is one of rotation and
% scaling. There are certain vectors, however, which in some sense
% _resonate_ with some fundamental property of the matrix, and do not get
% rotated. These special vectors (or directions in N-Dimensional space) are
% called *eigenvectors* and the scaling factor by which they are stretched
% are called the *eigenvalues*. Mathematically what we are saying is:
% $$\bf{A}x = \lambda x$
% Or visually we are saying:
evecPlot
%%
% Essentially, what we are saying is that (square) matrices have certain
% ``resonant modes'' or ``preferred'' co-ordinate directions, which we call
% _eigenvectors_ of the matrix. The eigenvectors are orthogonal to one
% another, and together form an orthogonal basis. If an $$N\times N$ matrix
% has N distinct eigenvalues, and correspondingly N orthogonal
% eigenvectors, then it is said to have *Rank* N, or to be *full rank*.

%%
% To make all this more concrete, let's consider an example from the
% mathematical theory of waves and vibration. In one dimension, the wave
% equation can be written as the following _partial differential equation_
% (don't worry about what this means, exactly, you'll learn more about all
% this later).
%%
% <latex>{\Large $\frac{\partial ^2 \Psi}{\partial t^2} = c\frac{\partial ^2
% \Psi}{\partial x^2}$}</latex>
%%
% We solve such a system by a process known as _separation of the
% variables_, and this involves writing the solution to the above equation
% as a product of two parts, one purely dependent on space and the other on
% time:
%%
% $${\Large\Psi\left(x,t\right) = \Theta\left(x\right)\Phi\left(t\right)}$
%%
% By substituting this form of the solution back into the differential
% equation, we get:
%%
% $$\ddot{\Phi}(t)\Theta(x)=c\Phi(t)\Theta''(x)$
%%
% or that
%%
% <latex> {\Large$\frac{\ddot{\Phi}(t)}{\Phi(t)} =
% c\frac{\Theta''(x)}{\Theta(x)} = -k^2 $}</latex>
%%
% The only way that the left hand side, which is completely independent of
% $$x$, can be equal to the right hand side, which is completely
% independent of $$t$, is if both sides are equal to a constant, $$-k^2$.
% We are interested in time independent solutions (standing waves) for this
% problem, so we can consider only the spatial parts of the equation, which
% can be written as:
%%
% <latex>
% {\Large$\frac{\partial ^2 \Theta(x)}{\partial x^2} = -k^2\Theta(x)
% $ }</latex>
%%
% This is an eigenvalue equation, exactly as we had above, and the
% solutions we are looking for are called *eigenfunctions*. This particular
% equation is important in physics and is known as the _Helmholtz
% equation_.
%%
% Using the second derivative differentiation matrix we described above
% (actually a more accurate version of it) and extending the notion to two
% dimensions, we transform a differential equation into a matrix eigenvalue
% equation. We can then use MATLAB to compute the eigenvalues and
% eigenvectors numerically to investigate the standing wave patterns, for
% example:
chladni

%   Copyright 2008-2009 The MathWorks, Inc.
%   $Revision: 35 $  $Date: 2009-05-29 15:27:34 +0100 (Fri, 29 May 2009) $


function a = lehmer ( m, n )

%*****************************************************************************80
%
%% LEHMER returns the LEHMER matrix.
%
%  Discussion:
%
%    This matrix is also known as the "Westlake" matrix.
%
%  Formula:
%
%    A(I,J) = min ( I, J ) / max ( I, J )
%
%  Example:
%
%    N = 5
%
%    1/1  1/2  1/3  1/4  1/5
%    1/2  2/2  2/3  2/4  2/5
%    1/3  2/3  3/3  3/4  3/5
%    1/4  2/4  3/4  4/4  4/5
%    1/5  2/5  3/5  4/5  5/5
%
%  Properties:
%
%    A is symmetric: A' = A.
%
%    Because A is symmetric, it is normal.
%
%    Because A is normal, it is diagonalizable.
%
%    A is positive definite.
%
%    A is totally nonnegative.
%
%    The inverse of A is tridiagonal.
%
%    The condition number of A lies between N and 4*N*N.
%
%    The family of matrices is nested as a function of N.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 October 2007
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Morris Newman, John Todd,
%    The evaluation of matrix inversion programs,
%    Journal of the Society for Industrial and Applied Mathematics,
%    Volume 6, Number 4, 1958, pages 466-476.
%
%    Solutions to problem E710, proposed by DH Lehmer: The inverse of
%    a matrix.
%    American Mathematical Monthly,
%    Volume 53, Number 9, November 1946, pages 534-535.
%
%    John Todd,
%    Basic Numerical Mathematics, Volume 2: Numerical Algebra,
%    Academic Press, 1977, page 154.
%
%  Parameters:
%
%    Input, integer M, N, the number of rows and columns of A.
%
%    Output, real A(M,N), the matrix.
%
  a = zeros ( m, n );

  for i = 1 : m
    for j = 1 : n
      a(i,j) = ( min ( i, j ) ) / ( max ( i, j ) );
    end
  end

  return
end


function mortrage()
clc;clear all; close all;
% definitions
% P = principle amount
% J = Monthly intrest rate
% R = Total monthly payment
% R = J*P+ amount applied to principle
% new principle amount
% P+j*P-R => P(1+J)-R => P*m-R: m=1+J

% inputs
pri=input('Principle amount ');
Yr=input(' No of Year ' );
rate=input('Annual percentage rate ' );
peryear=1/12;
percent=1/100;
ttl_mnt=Yr*12;
syms m J P R A N;

% the principal after n payments can be written as
% P = A?mn ? R? (mn ? 1)/(m? 1).
solve(A*m^N - R*(m^N - 1)/(m - 1), R);
R = subs(ans, m, J + 1);
format bank;
 disp( '     Interest Rate      Payment')
%for rate= 6:0.2:12
    disp([rate, double(subs(R, [A, N, J], [pri, ttl_mnt, rate*percent*peryear]))])
%end
ttl_payment=subs(R, [A, N, J], [pri, ttl_mnt, rate*percent*peryear])*ttl_mnt;
disp('total amunt paid =')
disp(ttl_payment)


%DEMO_PGAUSS  How to use PGAUSS
%
%   This script illustrates various properties of the Gaussian function.
%
%   .. figure::
%
%      Window+Dual+Tight
%
%      This figure shows an optimally centered Gaussian for a
%      given Gabor system, its canonical dual and tight windows
%      and the DFTs of these windows.
%
%   See also: pgauss

disp('Type "help demo_pgauss" to see a description of how this demo works.');

% A quick test: If the second input parameter to
% pgauss is not specified, the output will be
% invariant under an unitary DFT. Matlabs FFT is does not
% preserve the norm, so it must be scaled a bit.

L=128;
g=pgauss(L);

disp('');
disp('Test of DFT invariance: Should be close to zero.');
norm(g-dft(g))

% Setup parameters and length of signal.
% Note that it must hold that L=M*b=N*a for some integers
% b and N, and that a <= M
L=72;  % Length of signal.
a=6;   % Time shift.
M=9;   % Number of modulations.

% Calculate the frequency shift.
b=L/M;

% For this Gabor system, the optimally concentrated Gaussian
% is given by
g=pgauss(L,a/b);

% This is not invarient with respect to a DFT, but it is still
% real and whole point even
disp('');
disp('The function is WP even. The following should be 1.');
isevenfunction(g)

disp('Therefore, its DFT is real.');
disp('The norm of the imaginary part should be close to zero.');
norm(imag(dft(g)))

% Calculate the canonical dual.
gdual=gabdual(g,a,M);

% Calculate the canonical tight window.
gtight=gabtight(g,a,M);

% Plot them:

% Standard note on plotting:
%
% - All windows have real DFTs, but Matlab does not
%   always recoqnize this, so we have to filter away
%   the small imaginary part by calling REAL(...)
%
% - The windows are all centered around zero, but this
%   is not visually pleasing, so the window must be
%   shifted to the middle by an FFTSHIFT
%

gf_plot      = fftshift(real(dft(g)));
gdual_plot   = fftshift(gdual);
gdualf_plot  = fftshift(real(dft(gdual)));
gtight_plot  = fftshift(gtight);
gtightf_plot = fftshift(real(dft(gtight)));
figure(1);

subplot(3,2,1);
x=(1:L).';
plot(x,fftshift(g),'-',...
     x,circshift(fftshift(g),a),'-',...
     x,circshift(fftshift(g),-a),'-');
title('g=pgauss(72,6/8)');

subplot(3,2,2);
plot(gf_plot);
title('g, frequency domain');

subplot(3,2,3);
plot(gdual_plot);
title('Dual window of g');

subplot(3,2,4);
plot(gdualf_plot);
title('dual window, frequency domain');

subplot(3,2,5);
plot(gtight_plot);
title('Tight window generated from g');

subplot(3,2,6);
plot(gtightf_plot);
title('tight window, frequency domain');


function copl = isCoplanar(x,y,z,tol)
%ISCOPLANAR Tests input points for coplanarity in 3-space.
%
% COPL = isCoplanar(PTS)
% Tests the coplanarity of the input points in array PTS. Input array must
% be 4-by-3, each row containing coordinate of one point.
%
% COPL = isCoplanar(PTS, TOLERANCE)
% Specifies the tolerance value used for checking coplanarity. Default is
% zero.
%
%
% Example:
%   iscoplanar([1 2 -2; -3 1 -14; -1 2 -6; 1 -2 -8], eps)

%
% Adapted from a function originally written by Brett Shoelson, Ph.D.
% brett.shoelson@joslin.harvard.edu
% https://fr.mathworks.com/matlabcentral/fileexchange/46-iscoplanar-m
%

if nargin == 0
	error('Requires at least one input argument.');

elseif nargin == 1
    if size(x,2) == 3
        % Matrix of all x,y,z is input
        pts = x;
        tol = 0;
    else
        error('Invalid input.')
    end

elseif nargin == 2
	if size(x,2) == 3
		% Matrix of all x,y,z is input
		pts = x;
		tol = y;
	else
		error('Invalid input.')
	end
elseif nargin == 3
	% Compile a matrix of all x,y,z
	pts = [x y z];
	tol = 0;
else
	pts = [x y z];
end

if size(x, 1) < 4
    error('Requires at least four points to compute coplanarity');
end

% replace first point at the origin and compute SVD of the matrix
sv = svd(bsxfun(@minus, pts(2:end,:), pts(1,:)));
copl = sv(3) <= tol * sv(1);

% % Alterantive version that computes the rank of the matrix
% rnk = rank(bsxfun(@minus, pts(2:end,:), pts(1,:)), tol);
% copl = rnk <= size(pts, 2) - 1;

% % Old version:
% %Compare all 4-tuples of point combinations; {P1:P4} are coplanar iff
% %det([x1 y1 z1 1;x2 y2 z2 1;x3 y3 z3 1;x4 y4 z4 1])==0
% tmp = nchoosek(1:size(pts,1),4);
% for ii = 1:size(tmp,1)
% 	copl = abs(det([pts(tmp(ii, :), :) ones(4,1)])) <= tolerance;
% 	if ~copl
% 		break
% 	end
% end


function sig = signature(b,x0,y0)
%SIGNATURE Computes the signature of a boundary.
%   SIG = SIGNATURE(B,X0,Y0) computes the signature of boundary. B. A
%   signature is defined as the distance (DIST) from (X0,Y0) to the
%   boundary, as a function of angle. B is an np-by-2 matrix, with np >=
%   360, whose rows contain the (x,y) = (row,col) coordinates of the
%   boundary ordered in a clockwise or counterclockwise direction. If
%   (X0,Y0) is not included in the input argument, the centroid of the
%   boundary is used by default. SIG is a vector of size size 360-by-1,
%   indicating a signature resolution of one degree. The input must be a
%   one-pixel-thick boundary obtained, for example, by using function
%   bwboundaries.
%
%   If (X0,Y0) or the default centroid is outside the boundary, the
%   signature is not defined and an error is issued.
%
%   Copyright 2002-2020 Gatesmark
%
%   This function, and other functions in the DIPUM Toolbox, are based
%   on the theoretical and practical foundations established in the
%   book Digital Image Processing Using MATLAB, 3rd ed., Gatesmark
%   Press, 2020.
%
%   Book website: http://www.imageprocessingplace.com
%   License: https://github.com/dipum/dipum-toolbox/blob/master/LICENSE.txt

% Check dimensions of b.
[np,nc] = size(b);
if (np < 360 || nc ~= 2)
   error('b must be of size (>=360)-by-2.');
end

% Some boundary tracing programs, such as bwboundaries.m, result in a
% sequence in which the coordinates of the first and last points are the
% same. If this is the case, in b, eliminate the last point.
if isequal(b(1,:),b(np,:))
   b = b(1:np - 1,:);
   np = np - 1;
end

% If (x0,y0) is not specified, set the origin from which the signature
% is computed as the centroid.
if nargin == 1
   % Coordinates of the centroid.
   x0 = sum(b(:,1))/np;
   y0 = sum(b(:,2))/np;
end

% Check to see that (x0,y0) is inside the boundary.
IN = inpolygon(x0,y0,b(:,1),b(:,2));
if ~IN
   error('(x0,y0) or centroid is not inside the boundary.')
end

% Shift origin of coordinate system to (x0,y0).
b(:,1) = b(:,1) - x0;
b(:,2) = b(:,2) - y0;

% Convert the coordinates to polar. But first have to convert the given
% image coordinates, (x,y), to the coordinate system used by MATLAB for
% conversion between Cartesian and polar cordinates. Designate these
% coordinates by (xcart,ycart). The two coordinate systems are related
% as follows:  xcart = y and ycart = -x, where (x,y) = (row,col).
xcart = b(:,2);
ycart = -b(:,1);
[theta,rho] = cart2pol(xcart,ycart);

% Convert angles to degrees.
theta = theta.*(180/pi);

% Convert to all nonnegative angles.
j = theta == min(theta(:));
k = theta == max(theta(:));
theta = theta.*(0.5*abs(1 + sign(theta)))...
   - 0.5*(-1 + sign(theta)).*(360 + theta);

% Set the smallest angle to 0 and the largest angle to 359.
theta(j) = 0;
theta(k) = 359;

% Sort angles in ascending order and pair them with the corresponding
% rho
[theta,idx] = sort(theta);
rho(idx) = rho;
n = size(theta,1);

% Subsample the signature into 360 angle increments.
n = size(rho,1);
idx = round(linspace(1,n,360));
rho = rho(idx);
sig = rho;


% This is a small script to compute the Bethe approximation of a small
% factor graph. The factor graph is generated by taking the Bayes net of
% Lauritzen and Spiegelhalter (1988) and setting the following random
% variables as evidence
%
%   visit to Asia  = false
%   positive X-ray = true
%   smoking        = true
%
% For this particular set of evidence, the Bethe approximation is exact
% because the corresponding factor graph does not have any cycles.
%
% For more information, see: Lauritzen and Speigelhalter (1988). "Local
% computations with probabilities on graphical structures and their
% application to expert systems." Journal of the Royal Statistical
% Society, Series B, Vol. 50, No. 2, pp. 157-194.
%
% Copyright (C) 2007 Peter Carbonetto. All Rights Reserved.
% This code is published under the Eclipse Public License.
%
% Author: Peter Carbonetto
%         Dept. of Computer Science
%         University of British Columbia
%         May 19, 2007

verbose = true;

% The remaining random variables can either be true or false. They are:
%   1. tuberculosis
%   2. lung cancer
%   3. bronchitis
%   4. tuberculosis or lung cancer
%   5. dysponoea
nv = 5;             % The number of random variables.
nf = 6;             % The number of factors.
K  = 2*ones(nv,1);  % The number of possible discrete assignments to the
                    % random variable at each site.
C = cell(nf,1);     % The factor neighbourhoods.
f = cell(nf,1);     % The factors.

% Set up the factors.
% ------------------
% This is p(tuberculosis | visit to Asia = false).
C{1} = 1;
f{1} = [ 0.01 0.99 ]';

% This is p(lung cancer | smoking = true).
C{2} = 2;
f{2} = [ 0.1 0.9 ]';

% This is p(bronchitis | smoking = true).
C{3} = 3;
f{3} = [ 0.6 0.4 ]';

% This is p(tuberculosis or lung cancer | tuberculosis, lung cancer).
C{4}     = [4 1 2];
p        = zeros(2,2,2);
p(:,1,1) = [ 0.99 0.01 ]';
p(:,1,2) = [ 0.99 0.01 ]';
p(:,2,1) = [ 0.99 0.01 ]';
p(:,2,2) = [ 0.99 0.01 ]';
f{4}     = p;

% This is p(positive X-ray = true | tuberculosis or lung cancer).
C{5} = 4;
f{5} = [ 0.98 0.5 ]';

% This is p(dyspnoea | tuberculosis or lung cancer, bronchitis).
C{6}     = [5 4 3];
p        = zeros(2,2,2);
p(:,1,1) = [ 0.9 0.1 ]';
p(:,1,2) = [ 0.7 0.3 ]';
p(:,2,1) = [ 0.8 0.2 ]';
p(:,2,2) = [ 0.1 0.9 ]';
f{6}     = p;
clear p

% Set up the junction graph.
% -------------------------
% These are the large regions of the junction graph.
Rv{1} = [1 2 4];
Rf{1} = [1 2 4];
NR{1} = [1];

Rv{2} = [3 4 5];
Rf{2} = [3 5 6];
NR{2} = [1];

% These are the small regions of the junction graph.
Sv{1} = [4];
Sf{1} = [];
NS{1} = [1 2];

nr = length(Rv);  % The number of large regions.
ns = length(Sv);  % The number of small regions.

% Infer the region marginals.
% --------------------------
% Compute the junction graph approximation to the variational free
% energy. Since the junction graph is a tree in this case (trivially,
% because there are only two large regions), the junction graph
% approximation is exact.
qR = bopt(K,C,f,Rv,Rf,Sv,Sf,NS,verbose);

% Output the marginal probabilities.
% ---------------------------------
p = ndsum(qR{1},[2 3]);
fprintf('Pr(tuberculosis | evidence) = %0.2f \n', p(1));

p = ndsum(qR{1},[1 3]);
fprintf('Pr(lung cancer | evidence)  = %0.2f \n', p(1));

p = ndsum(qR{2},[2 3]);
fprintf('Pr(bronchitis | evidence)   = %0.2f \n', p(1));

p = ndsum(qR{2},[1 2]);
fprintf('Pr(dysponoea | evidence)    = %0.2f \n', p(1));


function [ fea, out ] = ex_convdiff3( varargin )
%EX_CONVDIFF3 1D Time dependent convection and diffusion equation example.
%
%   [ FEA, OUT ] = EX_CONVDIFF3( VARARGIN ) 1D time dependendt convection and diffusion equation on
%   a line with an infinately oscillating exact solution. Accepts the following property/value pairs.
%
%       Input       Value/{Default}        Description
%       -----------------------------------------------------------------------------------
%       w           scalar {1.5*pi}        Simulation parameter
%       U           scalar {2}             Simulation parameter
%       a           scalar {1}             Convection velocity
%       nu          scalar {0.1}           Diffusion coefficient
%       hmax        scalar {1/25}          Max grid cell size
%       dt          scalar {0.02}          Time step size
%       ischeme     scalar {3}             Time stepping scheme
%       sfun        string {sflag2}        Shape function
%       iplot       scalar 0/{1}           Plot solution (=1)
%                                                                                .
%       Output      Value/(Size)           Description
%       -----------------------------------------------------------------------------------
%       fea         struct                 Problem definition struct
%       out         struct                 Output struct

% Copyright 2013-2022 Precise Simulation, Ltd.

cOptDef = { ...
  'w',        1.5*pi; ...
  'U',        2; ...
  'a',        1; ...
  'nu',       0.1; ...
  'hmax',     1/25; ...
  'dt'        0.02; ...
  'ischeme'   3; ...
  'sfun',     'sflag1'; ...
  'iplot',    1; ...
  'tol',      3e-2; ...
  'fid',      1 };
[got,opt] = parseopt(cOptDef,varargin{:});
fid       = opt.fid;

w  = opt.w;
U  = opt.U;
a  = opt.a;
nu = opt.nu;
l1 = ['(',num2str(a),'+sqrt(',num2str(a^2),'+',num2str(4*nu*w),'*i))/',num2str(2*nu)];
l2 = ['(',num2str(a),'-sqrt(',num2str(a^2),'+',num2str(4*nu*w),'*i))/',num2str(2*nu)];
refsol  = ['(exp(',l1,'*x)-exp(',l2,'*x))/(exp(',l1,')-exp(',l2,'))*',num2str(U),'*exp(i*',num2str(w),'*t)'];
refsol0 = ['(exp(',l1,'*x)-exp(',l2,'*x))/(exp(',l1,')-exp(',l2,'))*',num2str(U)];

% Grid generation.
fea.grid = linegrid( 1/opt.hmax, 0, 1 );

% Problem definition.
fea.sdim  = { 'x' };
fea = addphys( fea, @convectiondiffusion );
fea.phys.cd.sfun = { opt.sfun };
fea.phys.cd.eqn.coef{2,4} = { opt.nu };
fea.phys.cd.eqn.coef{3,4} = { opt.a  };
fea = parsephys(fea);

% Parse and solve problem.
fea = parseprob( fea );
fea.bdr.d{1} = 0;
fea.bdr.d{2} = [num2str(U),'*cos(',num2str(w),'*t)'];
fea.bdr.n    = cell(1,2);
x = fea.grid.p';
if( strcmp( opt.sfun,'sflag2' ) )
  x = [ x; (x(2:end)+x(1:end-1))/2 ];
end
init = real( eval( refsol0 ) );
[fea.sol.u,tlist] = solvetime( fea, 'fid', fid, 'init', init, 'ischeme', opt.ischeme, 'tstep', opt.dt, 'tmax', 1 );

% Postprocessing.
if( opt.iplot>0 )
  figure;
  if( opt.iplot>1 )
    i_sol_list = 1:numel(tlist);
  else
    i_sol_list = numel(tlist);
  end
  [~,ix] = sort( x );
  for i_sol=i_sol_list
    t = tlist(i_sol);
    clf
    postplot( fea, 'surfexpr', 'c', 'solnum', i_sol );
    hold on
    u_r = real( eval( refsol ) );
    plot( sort(x), u_r(ix), 'r--' );
    title( ['Solution at time ',num2str(t)])
    xlabel( 'x' )
    drawnow
  end
end

% Error checking.
for i_sol=1:numel(tlist)
  u_i = fea.sol.u(:,i_sol);
  t   = tlist(i_sol);
  u_r = real( eval( refsol ) );
  errnm(i_sol) = norm( u_i - u_r )/norm( u_r );
end
out.err  = errnm;
out.pass = all( errnm<opt.tol );

if ( nargout==0 )
  clear fea out
end


% SubgradientOpt.m // Jon Lee
%
% Apply Subgradient Optimization to
%   z = min c'x
%        Ex  = h
%        Ax  = b
%         x >= 0
%
% NOTE: WE ONLY HANDLE PROBLEMS WITH BOUNDED SUBPROBLEM LPs.
%       IF WE ENCOUNTER AN UNBOUNDED SUBPROBLEM, THEN WE QUIT.

% generate a random example
n = 50; % number of variables
m1 = 15; % number of equations to relax
m2 = 10; % number of equations to keep

rng('default');
rng(1);  % set seed
E = rand(m1,n);  % random m1-by-n matrix
A = rand(m2,n);  % random m2-by-n matrix

w = rand(n,1);     % generate rhs's so problem is feasible
h = E*w;
b = A*w;

c = rand(n,1);     % generate random objective
% first we will calculate the optimal value z of the original LP, just to
% compare later
disp('Optimizing the original LP without Lagrangian relaxation');
[x,z,exitflag] = linprog(c,[],[],[E; A],[h; b],zeros(n,1),[]);
if (exitflag < 1)
     disp('fail 1: original LP (without using LR) did not solve correctly');
     return;
end;

disp('Optimal value of the original LP:');
z

disp('Starting Subgradient Optimization');

MAXIT = 100; % number of iterations

results1 = zeros(MAXIT,1);
results2 = zeros(MAXIT,1);

k = 0; % iteration counter
y = zeros(m1,1); % initialize y as you like
g = zeros(m1,1); % initialize g arbitrarily --- it will essentially be ignored
stepsize = 0;    % just to have us really start at the initialized y
bestlb = -Inf;
while k < MAXIT
   k = k + 1; % increment the iteration counter
   y = y + stepsize*g; % take a step in the direction of the subgradient
   % solve the Lagrangian
   [x,subval,exitflag,output,dualsol] = linprog((c'-y'*E)',[],[],A,b,zeros(n,1),[]);
   v = h'*y + (c'-y'*E)*x;  % calculate the value of the Lagrangian
   disp(v);
   bestlb = max(bestlb,v);
   results1(k)=k;
   results2(k)=v;
   g = h - E*x; % calculate the subgradient
   stepsize = 1/k; % calculate the step size
end

bestlb

z

clf;  % clear figure
% plot the sequence of MAXIT lower bounds
plot(results1,results2,'k--.','MarkerSize',15);
hold on;
% plot a horizontal line at height z
plot([1,MAXIT],[ z z ], 'r-','LineWidth',2.5)
axis tight;
xlabel('Iteration');
ylabel('Lagrangian lower bound');
legend('Lagrangian LB','z','Location','SouthEast');

print -djpeg SubgradientOpt.jpeg;

% Next, if y really solves the Lagrangian dual, and pi is the
% optimal solution to the Lagrangian subproblem corresponding to y,
% then y and pi should be an optimal dual solution to the given problem.
%
% Let's see if y and pi are nearly dual feasible.

pi = - dualsol.eqlin;
Total_Dual_Infeasibility = norm(min(c' - y'*E - pi'*A,zeros(1,n)))


function [H,info] = freqwavelet(name,L, varargin)
%FREQWAVELET  Wavelet in the freq. domain
%   Usage: H=freqwavelet(name,L)
%          H=freqwavelet(name,L,scale)
%          [H,info]=freqwavelet(...)
%
%   Input parameters:
%         name  : Name of the wavelet
%         L     : System length
%   Output parameters:
%         H     : Frequency domain wavelet
%         info  : Struct with additional outputs
%
%   `freqwavelet(name,L)` returns peak-normalized "mother" frequency-domain
%   wavelet `name` for system length *L*. The basic scale is selected such
%   that the peak is positioned at the frequency 0.1 relative to the Nyquist
%   rate (fs/2).
%
%   The supported wavelets that can be used in place of `name` (the actual
%   equations can be found at the end of this help):
%
%     'cauchy'    Cauchy wavelet with alpha=100. Custom order=(alpha-1)/2
%                 (with alpha>1) can be set by `{'cauchy',alpha}`. The
%                 higher the order, the narrower and more symmetric the
%                 wavelet. A numericaly stable formula is used in order
%                 to allow support even for very high `alpha` e.g.
%                 10^6.
%
%     'morse'     Morse wavelet with alpha=100 and gamma=3. Both parameters
%                 can be set directly by `{'morse',alpha,gamma}`. `alpha`
%                 has the same role as for 'cauchy', `gamma` is the
%                 'skewness' parameter. 'morse' becomes 'cauchy' for
%                 gamma=1.
%
%     'morlet'    Morlet wavelet with sigma=4. The parameter `sigma`
%                 is the center frequency to standard deviation ratio of
%                 the main generating Gaussian distribution. Note that the
%                 true peak and standard deviation of the Morlet wavelet
%                 differ, in particular for low values of `sigma` (<5).
%                 This is a consequence of the correction factor. The
%                 parameter can be set directly by `{'morlet',sigma}`.
%
%    'fbsp'       Frequency B-spline wavelet of order m=3, center frequency
%                 to bandwidth ratio fb = 2. The parameters can be set
%                 by `{'fbsp',m,fb}`. Note that `m` must be integer and
%                 greater than or equal to 1, and `fb` must be greater than
%                 or equal to 2.
%
%    'analyticsp' Positive frequency part of cosine-modulated B-spline
%                 wavelet of  order order=3, with center frequency to main
%                 lobe width ratio fb = 1. The parameters can be set by
%                 `{'analyticsp',order,fb}`. Note that `order` and `fb`
%                 must be integer and greater than or equal to 1.
%
%    'cplxsp'     Complex-modulated B-spline of order order=3, with center
%                 frequency to main lobe width ratio fb = 1. The parameters
%                 can be set by `{'cplxsp',order,fb}`. Note that `order`
%                 and `fb` must be integer and greater than or equal to 1.
%
%   `freqwavelet(name,L,scale)` returns a "dilated" version of the wavelet.
%   The nonzero scalar `scale` controls both the bandwidth and the center
%   frequency. Values greater than 1 make the wavelet wider (and narrower
%   in the frequency domain), values lower than 1 make the wavelet narrower.
%   The center frequency is moved to`0.1/scale`. The center frequency is
%   limited to the range of "positive" frequencies ]0,1] (up to Nyquist rate).
%   If `scale` is a vector, the output is a `L x numel(scale)` matrix with
%   the individual wavelets as columns.
%
%   The following additional flags and key-value parameters are available:
%
%     'scale'           Wavelet scale (relative to basic scale)
%
%     'waveletParams'   a vector containing the respective wavelet parameters
%                       [alpha, beta, gamma] for cauchy and morse wavelets
%                       [sigma] for morlet wavelets
%                       [order, fb] for splines
%
%     'basefc',fc      Normalized center frequency of the mother wavelet
%                      (scale=1). The default is 0.1.
%
%     'bwthr',bwthr    The height at which the bandwidth is computed.
%                      The default value is 10^(-3/10) (~0.5).
%
%     'efsuppthr',thr  The threshold determining the effective support of
%                      the wavelet. The default value is 10^(-5).
%
%     'scal',s         Scale the filter by the constant *s*. This can be
%                      useful to equalize channels in a filter bank.
%
%     'delay',d        Set the delay of the filter. Can be either a scalar or
%                      a vector of the same length as *scale*. Default value is zero.
%
%   The admissible range of scales can be adjusted to handle different
%   scenarios:
%
%     'positive'       Enables the construction of wavelets at postive
%                      center frequencies ]0,1]. If `basefc=0.1`, this
%                      corresponds to scales larger than or equal to 0.1.
%                      This is the default.
%
%     'negative'       Enables the construction of wavelets at negative
%                      center frequencies [-1,0[. If `basefc=0.1`, this
%                      corresponds to scales smaller than or equal to -0.1.
%
%     'analytic'       Enables the construction of wavelets with center
%                      frequencies in ]0,2] for analysis of analytic signals.
%                      [! This feature is currently experimental and may not
%                      always work as intended. !]
%
%   The format of the output is controlled by the following flags:
%   'full' (default),'econ','asfreqfilter':
%
%     'full'           The output is a `L x numel(scale)` matrix.
%
%     'econ'           The output is a `numel(scale)` cell array with
%                      individual freq. domain wavelets truncated to the
%                      length of the effective support given by parameter 'efsuppthr'.
%                      Does not run stably for system lengths > 2000
%
%     'asfreqfilter'   As 'econ', but the elements of the cell-array are
%                      filter structs with fields .H and .foff as in
%                      |blfilter| to be used in |filterbank| and related.
%
%   `[H,info]=freqwavelet(...)` additionally returns a struct with the
%   following fields:
%
%     .fc             Normalized center frequency.
%
%     .foff           Index of the first sample above the effective
%                     support threshold (minus one).
%                     It can be directly used to convert the 'econ'
%                     output to 'full' by `circshift(postpad(H,L),foff)`.
%
%     .fsupp          Length of the effective support (with values above
%                     efsuppthr).
%
%     .basefc         Center frequency of the implied mother wavelet.
%
%     .scale          The scale used.
%
%     .dilation       The actual dilation used in the formula.
%
%     .bw             Relative bandwidth at -3 dB (half of the height).
%
%     .tfr            Time-frequency ratio of a Gaussian with the same
%                     bandwidth as the wavelet.
%
%     .aprecise       Exact natural subsampling factors (not rounded).
%
%     .a_natural      Fractional natural subsampling factors in the
%                     format acceptable by |filterbank| and related.
%
%     .cauchyAlpha    Alpha value of closest Cauchy wavelet [NOTE: Not
%                     implemented for non-Morse wavelets.]
%
%   Additionally, the function accepts flags to normalize the output.
%   Please see the help of |setnorm|. By default, no normaliazation is
%   applied.
%
%   Wavelet definitions
%   -------------------
%
%   `C` is a normalization constant.
%
%   Cauchy wavelet
%
%       H = C \xi^{\frac{\alpha-1}{2}} exp( -2\pi\xi )
%
%       .. math:: H = C \xi^{\frac{\alpha-1}{2}} exp( -2\pi\xi )
%
%   Morse wavelet
%
%       H = C \xi^{\frac{\alpha-1}{2\gamma}} exp( -2\pi\xi^{\gamma} )
%
%       .. math:: H = C \xi^{\frac{\alpha-1}{2\gamma}} exp( -2\pi\xi^{\gamma} )
%
%   Morlet wavelet
%
%       H = C \xi^{\frac{\alpha-1}{2\gamma}} exp( -2\pi\xi^{\gamma} )
%
%       .. math:: H = C \xi^{\frac{\alpha-1}{2\gamma}} exp( -2\pi\xi^{\gamma} )
%
%   Frequency bandlimited spline wavelet
%
%       H = C B (\xi - m \frac{\xi}{4})
%
%       .. math:: H = C B (\xi - m \frac{\xi}{4})
%
%   Analytic spline wavelet
%
%       H = C exp(-j \omega x) A(-exp(j \omega)) H(exp(-j \omega)
%
%       .. math:: H = C exp(-j \omega x) A(-exp(j \omega)) H(exp(-j \omega))
%
%   Complex spline wavelet
%
%       H = C exp(-j \omega x + \xi )
%
%       .. math:: H = C exp(-j \omega x + \xi )
%
%
%   References: rioul92 unalsc94
%
%   See also: setnorm, filterbank, blfilter

complainif_notenoughargs(nargin,2,upper(mfilename));

%set default input parameters
if ~isscalar(L)
    error('%s: L must be a scalar',upper(mfilename));
end

if ~iscell(name), name = {name}; end

freqwavelettypes = getfield(arg_freqwavelet(),'flags','wavelettype');

if ~ischar(name{1}) || ~any(strcmpi(name{1},freqwavelettypes))
  error('%s: First input argument must be the name of a supported window.',...
        upper(mfilename));
end

definput.import={'setnorm', 'freqwavelet'};
definput.importdefaults={'null'};
definput.keyvals.scale = 1;
definput.keyvals.scal = [];
definput.keyvals.basefc = 0.1;
definput.keyvals.delay=0;
definput.keyvals.fc = [];
definput.keyvals.bwthr = 10^(-3/10);
definput.keyvals.efsuppthr = 10^(-5);
definput.flags.freqrange = {'positive','negative','analytic'};
definput.flags.outformat = {'full','econ','asfreqfilter'};
definput.keyvals.fs = 2;
definput.keyvals.alphaStep = definput.keyvals.fs/L;

if ~iscell(name)
    name = {name};
end

[flags,kv,scale]=ltfatarghelper({'scale'},definput,varargin,'freqwavelet');

%check L
if ~isscalar(L)
    error('%s: L must be a scalar',upper(mfilename));
end

%check basefc
if ~isscalar(kv.basefc)
    error('%s: basefc must be a positive scalar',upper(mfilename));
end

scale = scale(:).'; % Scale should always be a row vector
if ~isnumeric(scale), error('%s: scale must be numeric',upper(mfilename)); end

if isempty(kv.scal)
    kv.scal = scale;
elseif ~isnumeric(kv.scal)
    error('%s: scal must be numeric',upper(mfilename));
elseif numel(kv.scal) ~= numel(scale)
    error('%s: scal must have exactly as many entries as scale',upper(mfilename));
end

if kv.alphaStep > 0.02
    error('%s: wavelet sampling too small. increase system length.',upper(mfilename));
end

%if L/scale > 10
%   error('%s: scale too large.',upper(mfilename));
%end

% Check range of scales
if flags.do_positive && (any(scale <= 0) || any(kv.basefc./scale > 1))
    error('%s: Frequency range flag is set to positive. scale must be positive and not smaller than basefc.', upper(mfilename));
end
if flags.do_negative && (any(scale >= 0) || any(kv.basefc./scale < -1))
    error('%s: Frequency range flag is set to negative. scale must be negative and not larger than -basefc.', upper(mfilename));
end
if flags.do_analytic && (any(scale <= 0) || any(kv.basefc./scale > 2))
    error('%s: Frequency range flag is set to analytic. scale must be positive and not smaller than 2*basefc.', upper(mfilename));
end

% Check delay vector
if numel(kv.delay) == 1
    kv.delay = repmat(kv.delay, 1, numel(scale));
end
if numel(kv.delay) ~= numel(scale)
    error('%s: delay must have exactly as many entries as scale',upper(mfilename));
end
% Check other parameters
if kv.efsuppthr < 0, error('%s: efsuppthr must be nonnegative',upper(mfilename)); end
if kv.bwthr < 0, error('%s: bwthr must be nonnegative',upper(mfilename)); end
if kv.bwthr < kv.efsuppthr, error('%s: efsuppthr must be lower than bwthr.',upper(mfilename)); end

M = numel(scale);

if flags.do_full
    H = zeros(L,M);
else
    H = cell(1,M);
end

if flags.do_negative
    wltype = 'negative';
else
    wltype = 'positive';
end

%% generate the wavelet prototype
[fun, fsupp_, peakpos, cauchyAlpha] = helper_waveletgeneratorfunc(name, wltype);

%% calculate the support as f(scale)
fsupp = zeros(5,M);
fsupp(5,:) = kv.fs;
basedil = peakpos/(kv.basefc);

if kv.efsuppthr > 0
    fsupp(1,:) = max(0,(fsupp_(1)/basedil)./scale);
    fsupp(5,:) = min(kv.fs,(fsupp_(5)/basedil)./scale);
end
fsupp(2,:) = max(0,(fsupp_(2)/basedil)./scale);
fsupp(3,:) = (fsupp_(3)/basedil)./scale;
fsupp(4,:) = min(kv.fs,(fsupp_(4)/basedil)./scale);

if flags.do_negative
    fsupp = flip(fsupp);
end

fsuppL = fsuppL_inner(fsupp,kv.fs,L,1:5);

%% calculate H
if flags.do_full
    if ~flags.do_negative
        y = ((0:L-1)').*basedil*kv.alphaStep*scale;
    else
        y = ([0;(L-1:-1:1)']).*basedil*kv.alphaStep*scale;
    end
    H = abs(kv.scal).*setnorm(fun(y), flags.norm);
elseif flags.do_econ
    for ii = 1:numel(scale)
        y = ((fsuppL(1,ii):fsuppL(end,ii)-1)').*basedil*kv.alphaStep*abs(scale(ii));
        H{ii} = abs(kv.scal(ii)).*setnorm(fun(y), flags.norm);%TODO: check output format: should be cell
    end
elseif flags.do_asfreqfilter
    for m = 1:numel(scale)
        y = @(L) ((fsuppL_inner(fsupp(:,m),kv.fs,L,1):fsuppL_inner(fsupp(:,m),kv.fs,L,5)-1)').*basedil*abs(scale(m))*kv.fs/L;
        H{m} = struct('H',@(L) abs(kv.scal(m))*setnorm(fun(y(L)),flags.norm),'foff',@(L)fsuppL_inner(fsupp(:,m),kv.fs,L,1),'realonly',0, 'delay', kv.delay(m));
   end
end

%% write info struct
info.basefc = kv.basefc;
info.fc = fsupp(3,:);
info.scale = scale;%';
info.dilation = basedil.*scale;%';
info.fsupp = fsuppL(end,:) - fsuppL(1,:) + ones(1,numel(scale));
if info.fsupp <= 0, info.fsupp = 0; end
info.bw  = (fsupp(4,:) - fsupp(2,:));
bwinsamples = info.bw./kv.alphaStep;
info.aprecise = L./bwinsamples;
info.a_natural(:,2) = ceil(bwinsamples);
info.a_natural = info.a_natural';
info.tfr = (cauchyAlpha - 1)./(pi*info.fc.^2*L);
info.cauchyAlpha = cauchyAlpha;
info.foff = fsuppL(1,:);

%if M==1 && iscell(H)
%    H = H{1};
%end

end

function fsuppL = fsuppL_inner(fsupp,fs,L,idx)
    fsuppL_all = [ ceil(fsupp(1:2,:)/fs*L); round(fsupp(3,:)/fs*L); floor(fsupp(4:5,:)/fs*L) ];
    fsuppL = fsuppL_all(idx,:);
end


function varargout = euclideanMST(points)
%EUCLIDEANMST Build euclidean minimal spanning tree of a set of points.
%
%   EDGES = euclideanMST(POINTS)
%   POINTS is a [NxP] array, N being the number of points and P being the
%   dimension.
%   Result EDGES is a [Mx2] array, containing indices of each vertex for
%   each edges.
%
%   [EDGES DISTS] = euclideanMST(POINTS)
%   Also returns the lengths of edges computed by MST algorithm.
%
%   Algorithm first computes Delaunay triangulation of the set of points,
%   then computes euclidean length of each edge of triangulation, and
%   finally uses prim algorithm to simplify the graph.
%
%   Example
%     % choose random points in the plane and display their Euclidean MST
%     pts = rand(50, 2)*100;
%     edges = euclideanMST(pts);
%     drawGraph(pts, edges)
%
%   See also
%   prim_mst, distancePoints, delaunayn
%

% ------
% Author: David Legland
% E-mail: david.legland@grignon.inra.fr
% Created: 2007-07-27, using Matlab 7.4.0.287 (R2007a)
% Copyright 2007-2022 INRA - BIA PV Nantes - MIAJ Jouy-en-Josas

% dimension
D   = size(points, 2);
Df  = factorial(D);

% compute all couples of vertices in unit triangle, tetrahedron, or n-dim
% simplex
subs = zeros(Df, 2);
k = 1;
for i = 1:D
    for j = i+1:D+1
        subs(k, 1) = i;
        subs(k, 2) = j;
        k = k + 1;
    end
end

% compute delaunay triangulation in D dimensions
tri = delaunayn(points);
Nt  = size(tri, 1);

% compute all possible edges
edges = zeros(Nt*Df, 2);
for t = 1:Nt
    for i = 1:Df
        edges((t-1)*Df+i, 1) = tri(t, subs(i, 1));
        edges((t-1)*Df+i, 2) = tri(t, subs(i, 2));
    end
end

% simplify edges
edges = unique(sort(edges, 2), 'rows');

% compute euclidean length of each edge
val = zeros(size(edges, 1), 1);
for i = 1:size(edges,1)
    val(i) = distancePoints(points(edges(i,1), :), points(edges(i,2), :));
end

% compute MST of created graph
[edges2, vals2] = prim_mst(edges, val);

% process output arguments
if nargout == 1
    varargout{1} = edges2;
elseif nargout==2
    varargout{1} = edges2;
    varargout{2} = vals2;
end


function [x]=grandn(a,b)

% function [x]=grandn(a,b)
% random number generator from the Gamma (a,b) distribution

% part 1: obtain a random number from Gamma(a,1)

if a>=1
x=gammadrawover1(a);
else
xtilde=gammadrawover1(a+1);
u=rand;
x=xtilde*u^(1/a);
end

% part 2: transform into a draw from G(a,b)
x=x*b;

% auxiliary nested function to draw a Gamma random number when argument 'a' is greater than or equal to 1
function x=gammadrawover1(a)
d=a-1/3;
c=1/((9*d)^0.5);
check=0;
   while check==0
   z=randn;
   u=rand;
   v=(1+c*z)^3;
      if (v>0 && log(u)<0.5*z^2+d-d*v+d*log(v))
      x=d*v;
      check=1;
      else
      check=0;
      end
   end
end

% declare end of general function
end


function [ intersect, p ] = plane_imp_line_par_int_3d ( a, b, c, d, x0, y0, z0, ...
  f, g, h )

%*****************************************************************************80
%
%% PLANE_IMP_LINE_PAR_INT_3D: intersection ( implicit plane, parametric line ) in 3D.
%
%  Discussion:
%
%    The implicit form of a plane in 3D is:
%
%      A * X + B * Y + C * Z + D = 0
%
%    The parametric form of a line in 3D is:
%
%      X = X0 + F * T
%      Y = Y0 + G * T
%      Z = Z0 + H * T
%
%    We normalize by choosing F*F+G*G+H*H=1 and 0 <= F.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 March 2005
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Adrian Bowyer and John Woodwark,
%    A Programmer's Geometry,
%    Butterworths, 1983, page 111.
%
%  Parameters:
%
%    Input, real A, B, C, D, the implicit plane parameters.
%
%    Input, real X0, Y0, Z0, F, G, H, parameters that define the
%    parametric line.
%
%    Output, logical INTERSECT, is TRUE if the line and the plane
%    intersect.
%
%    Output, real P(3), is a point of intersection of the line
%    and the plane, if INTERSECT is TRUE.
%
  dim_num = 3;
  tol = 0.00001;
%
%  Check.
%
  norm1 = sqrt ( a * a + b * b + c * c );

  if ( norm1 == 0.0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'PLANE_IMP_LINE_PAR_INT_3D - Fatal error!\n' );
    fprintf ( 1, '  The plane normal vector is null.\n' );
    error ( 'PLANE_IMP_LINE_PAR_INT_3D - Fatal error!' )
  end

  norm2 = sqrt ( f * f + g * g + h * h );

  if ( norm2 == 0.0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'PLANE_IMP_LINE_PAR_INT_3D - Fatal error!\n' );
    fprintf ( 1, '  The line direction vector is null.\n' );
    error ( 'PLANE_IMP_LINE_PAR_INT_3D - Fatal error!' )
  end

  denom = a * f + b * g + c * h;
%
%  The line and the plane may be parallel.
%
  if ( abs ( denom ) < tol * norm1 * norm2 )

    if ( a * x0 + b * y0 + c * z0 + d == 0.0 )
      intersect = 1;
      p(1) = x0;
      p(2) = y0;
      p(3) = z0;
    else
      intersect = 0;
      p(1:dim_num) = 0.0;
    end
%
%  If they are not parallel, they must intersect.
%
  else

    intersect = 1;
    t = - ( a * x0 + b * y0 + c * z0 + d ) / denom;
    p(1) = x0 + t * f;
    p(2) = y0 + t * g;
    p(3) = z0 + t * h;

  end

  return
end


function p = chebyshev(n,var)
%CHEBYSHEV    Chebyshev polynomial (exactly representable up to degree n<=81)
%
%   p = chebyshev(n,var)
%
%Computed by recursion
%  p(0,x) = 1
%  p(1,x) = x
%  p(n,x) = 2*x*p(n-1,x) - p(n-2,x)  for n>1
%
%Parameter var for dependent variable optional (default 'x')
%

% written  07/30/02     S.M. Rump
% modified 04/04/04     S.M. Rump  set round to nearest for safety
% modified 04/06/05     S.M. Rump  rounding unchanged
% modified 09/28/08     S.M. Rump  check for rounding to nearest improved
%

  e = 1e-30;
  if 1+e==1-e                           % fast check for rounding to nearest
    rndold = 0;
  else
    rndold = getround;
    setround(0)
  end

  if nargin==1
    var = 'x';
  else
    if ~ischar(var)
      error('variable must be string')
    end
  end

  if n==0
    p = polynom(1,var);
  elseif n==1
    p = polynom([1 0],var);
  else
    t1 = [1 zeros(1,n)];
    t2 = [0 1 zeros(1,n-1)];
    for i=3:n+1
      t3 = [0 2*t2(1:n)] - t1;
      t1 = t2;
      t2 = t3;
    end
    p = polynom(fliplr(t3),var);
  end

  if rndold
    setround(rndold)
  end


function pdf = gompertz_pdf ( x, a, b )

%*****************************************************************************80
%
%% GOMPERTZ_PDF evaluates the Gompertz PDF.
%
%  Discussion:
%
%    PDF(X)(A,B) = B * A**X / exp ( B * ( A**X - 1 ) / log ( A ) )
%
%    for
%
%      0.0 <= X
%      1.0 <  A
%      0.0 <  B
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    12 September 2004
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Johnson, Kotz, and Balakrishnan,
%    Continuous Univariate Distributions, Volume 2, second edition,
%    Wiley, 1994, pages 25-26.
%
%  Parameters:
%
%    Input, real X, the argument of the PDF.
%
%    Input, real A, B, the parameters of the PDF.
%    1 < A, 0 < B.
%
%    Output, real PDF, the value of the PDF.
%
  if ( x < 0.0 )

    pdf = 0.0;

  elseif ( 1.0 < a )

    pdf = exp ( log ( b ) + x * log ( a ) - ( b / log ( a ) ) * ( a^x - 1.0 ) );

  end

  return
end


function ASK_FSK_PSK(msglen)
%msglen= number of bits to be transmitted
%take msglen=10000, or 20000 for more accuracy
%If you have any problem or feedback please contact me @
%%===============================================
% NIKESH BAJAJ
% Asst. Prof., Lovely Professional University, India
% Almameter: Aligarh Muslim University, India
% +919915522564, bajaj.nikkey@gmail.com
%%===============================================
n=msglen;
b=randint(1,n);
f1=1;f2=2;
t=0:1/30:1-1/30;
%ASK
sa1=sin(2*pi*f1*t);
E1=sum(sa1.^2);
sa1=sa1/sqrt(E1); %unit energy
sa0=0*sin(2*pi*f1*t);
%FSK
sf0=sin(2*pi*f1*t);
E=sum(sf0.^2);
sf0=sf0/sqrt(E);
sf1=sin(2*pi*f2*t);
E=sum(sf1.^2);
sf1=sf1/sqrt(E);
%PSK
sp0=-sin(2*pi*f1*t)/sqrt(E1);
sp1=sin(2*pi*f1*t)/sqrt(E1);

%MODULATION
ask=[];psk=[];fsk=[];
for i=1:n
    if b(i)==1
        ask=[ask sa1];
        psk=[psk sp1];
        fsk=[fsk sf1];
    else
        ask=[ask sa0];
        psk=[psk sp0];
        fsk=[fsk sf0];
    end
end
figure(1)
subplot(411)
stairs(0:10,[b(1:10) b(10)],'linewidth',1.5)
axis([0 10 -0.5 1.5])
title('Message Bits');grid on
subplot(412)
tb=0:1/30:10-1/30;
plot(tb, ask(1:10*30),'b','linewidth',1.5)
title('ASK Modulation');grid on
subplot(413)
plot(tb, fsk(1:10*30),'r','linewidth',1.5)
title('FSK Modulation');grid on
subplot(414)
plot(tb, psk(1:10*30),'k','linewidth',1.5)
title('PSK Modulation');grid on
xlabel('Time');ylabel('Amplitude')
%AWGN
for snr=0:20
    askn=awgn(ask,snr);
    pskn=awgn(psk,snr);
    fskn=awgn(fsk,snr);

    %DETECTION
    A=[];F=[];P=[];
    for i=1:n
        %ASK Detection
        if sum(sa1.*askn(1+30*(i-1):30*i))>0.5
            A=[A 1];
        else
            A=[A 0];
        end
        %FSK Detection
        if sum(sf1.*fskn(1+30*(i-1):30*i))>0.5
            F=[F 1];
        else
            F=[F 0];
        end
        %PSK Detection
        if sum(sp1.*pskn(1+30*(i-1):30*i))>0
            P=[P 1];
        else
            P=[P 0];
        end
    end

    %BER
    errA=0;errF=0; errP=0;
    for i=1:n
        if A(i)==b(i)
            errA=errA;
        else
            errA=errA+1;
        end
        if F(i)==b(i)
            errF=errF;
        else
            errF=errF+1;
        end
        if P(i)==b(i)
            errP=errP;
        else
            errP=errP+1;
        end
    end
    BER_A(snr+1)=errA/n;
    BER_F(snr+1)=errF/n;
    BER_P(snr+1)=errP/n;
end

figure(2)
subplot(411)
stairs(0:10,[b(1:10) b(10)],'linewidth',1.5)
axis([0 10 -0.5 1.5]);grid on
title('Received signal after AWGN Channel')
subplot(412)
tb=0:1/30:10-1/30;
plot(tb, askn(1:10*30),'b','linewidth',1.5)
title('Received ASK signal');grid on
subplot(413)
plot(tb, fskn(1:10*30),'r','linewidth',1.5)
title('Received FSK signal');grid on
subplot(414)
plot(tb, pskn(1:10*30),'k','linewidth',1.5)
title('Received PSK signal');grid on
figure(3)
semilogy(0:20,BER_A, 'b','linewidth',2)
title('BER Vs SNR')
grid on;
hold on
semilogy(0:20,BER_F,'r','linewidth',2)
semilogy(0:20,BER_P, 'k','linewidth',2)
xlabel('Eo/No(dB)')
ylabel('BER')
hold off
legend('ASK','FSK','PSK');


% Optimal doping profile optimization with current gain constraint.
% Boyd, Kim, Vandenberghe, and Hassibi, "A tutorial on geometric programming"
% Joshi, Boyd, and Dutton, "Optimal doping profiles via geometric programming"
% Written for CVX by Almir Mutapcic 02/08/06
% (a figure is generated)
%
% Determines the optimal doping profile that minimizes base transit
% time subject to a lower bound constraint on the current gain (beta).
% This problem can be posed as a GP:
%
%   minimize   tau_B
%       s.t.   Nmin <= v <= Nmax
%              y_(i+1) + v_i^const1 <= y_i
%              w_(i+1) + v_i^const2 <= w_i, etc...
%              beta => beta_min
%
% where variables are v_i, y_i, and w_i.

% problem size
M = 20;

% problem constants
g1 = 0.42;
g2 = 0.69;
Nmax = 5*10^18;
Nmin = 5*10^16;
Nref = 10^17;
Dn0 = 20.72;
ni0 = 1.4*(10^10);
WB = 10^(-5);
C =  WB^2/((M^2)*(Nref^g1)*Dn0);

% minimum current gain values
beta_min_GE = [1 1.4 1.8 2.2 2.6 3.0 3.4 3.43]*(1e-11);

% exponent powers
pwi = g2 -1;
pwj = 1+g1-g2;

v_array = [];
for k = 1:length(beta_min_GE)
    fprintf( 'beta_min_GE = %g: ', beta_min_GE(k) );
    cvx_begin gp quiet
        % optimization variables
        variables v(M) y(M) w(M)

        % objective function is the base transmit time
        tau_B = C*w(1);

        minimize( tau_B )
        subject to
        % fixed problem constraints
        Nmin <= v <= Nmax;

        for i=1:M-1
            y(i+1) + v(i)^pwj <= y(i);
            w(i+1) + y(i)*v(i)^pwi <= w(i);
        end

        % equalities
        y(M) == v(M)^pwj;
        w(M) == y(M)*v(M)^pwi;

        % changing constraint
        (WB*beta_min_GE(k)/(M*Nref^(g1-g2)*Dn0))*y(1) <= 1;
    cvx_end
    fprintf( '%s\n', cvx_status );
    % keep the optimal solution
    v_array = [v_array v];
end

% plot the basic optimal doping profile
figure, clf
nbw = 0:1/M:1-1/M;
for k = 1:length(beta_min_GE)
  semilogy(nbw,v_array(:,k),'LineWidth',2); hold on;
end
axis([0 1 1e16 1e19]);
xlabel('base');
ylabel('doping');
text(0,Nmin,'Nmin ', 'HorizontalAlignment','right');
text(0,Nmax,'Nmax ', 'HorizontalAlignment','right');
hold off;


% CXCORR Circular Cross Correlation function estimates.
% CXCORR(a,b), where a and b represent samples taken over time interval T
% which is assumed to be a common period of two corresponded periodic signals.
% a and b are supposed to be length M row vectors, either real or complex.
%
% [x,c]=CXCORR(a,b) returns the length M-1 circular cross correlation sequence c
% with corresponded lags x.
%
% The circular cross correlation is:
%         c(k) = sum[a(n)*conj(b(n+k))]/[norm(a)*norm(b)];
% where vector b is shifted CIRCULARLY by k samples.
%
% The function doesn't check the format of input vectors a and b!
%
% For circular covariance between a and b look for CXCOV(a,b) in
% http://www.mathworks.com/matlabcentral/fileexchange/loadAuthor.do?objectType=author&objectId=1093734
%
% Reference:
% A. V. Oppenheim, R. W. Schafer and J. R. Buck, Discrete-Time Signal Processing,
% Upper Saddler River, NJ : Prentice Hall, 1999.
%
% Author: G. Levin, Apr. 26, 2004.

function [x,c]=CXCORR(a,b)
na=norm(a);
nb=norm(b);
a=a/na; %normalization
b=b/nb;
for k=1:length(b)
    c(k)=a*b';
    b=[b(end),b(1:end-1)]; %circular shift
end
x=[0:length(b)-1]; %lags


% CONVERT ROTATION ANGLES INTO A QUATERNION
function [q] = GetQuaternionFromEulers(eulers)
% THis function converts euler angle rotations into a
% quaternion via its components. Associated block:
% "Rotation Angles to Quaternions".
eulers = 0.5*eulers;
% Build vector of trigonometric arguements
trigArgs = [sin(eulers);cos(eulers)];
% Assemble Quaternion components
q = zeros(4,1);
q(1) = trigArgs(4)*trigArgs(5)*trigArgs(6) + trigArgs(1)*trigArgs(2)*trigArgs(3);
q(2) = trigArgs(4)*trigArgs(5)*trigArgs(3) - trigArgs(1)*trigArgs(2)*trigArgs(6);
q(3) = trigArgs(4)*trigArgs(2)*trigArgs(6) + trigArgs(1)*trigArgs(5)*trigArgs(3);
q(4) = trigArgs(1)*trigArgs(5)*trigArgs(6) - trigArgs(4)*trigArgs(2)*trigArgs(3);
% Renormalise
q = unit(q);
end


function [tfr,t,f] = tfrppage(x,t,N,h,trace);
%TFRPPAGE Pseudo Page time-frequency distribution.
%	[TFR,T,F]=TFRPPAGE(X,T,N,H,TRACE) computes the Pseudo Page
%	distribution of a discrete-time signal X, or the
%	cross Pseudo Page representation between two signals.
%
%	X     : signal if auto-PPage, or [X1,X2] if cross-PPage.
%	T     : time instant(s)          (default : 1:length(X)).
%	N     : number of frequency bins (default : length(X)).
%	H     : frequency smoothing window, H(0) being forced to 1.
%	                                 (default : Hamming(N/4)).
%	TRACE : if nonzero, the progression of the algorithm is shown
%	                                 (default : 0).
%	TFR   : time-frequency representation. When called without
%	        output arguments, TFRPPAGE runs TFRQVIEW.
%	F     : vector of normalized frequencies.
%
%	Example :
%	 sig=fmlin(128,0.1,0.4); tfrppage(sig);
%
%	See also all the time-frequency representations listed in
%	 the file CONTENTS (TFR*)

%	F. Auger, May-August 1994, July 1995.
%	Copyright (c) 1996 by CNRS (France).
%
%  This program is free software; you can redistribute it and/or modify
%  it under the terms of the GNU General Public License as published by
%  the Free Software Foundation; either version 2 of the License, or
%  (at your option) any later version.
%
%  This program is distributed in the hope that it will be useful,
%  but WITHOUT ANY WARRANTY; without even the implied warranty of
%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%  GNU General Public License for more details.
%
%  You should have received a copy of the GNU General Public License
%  along with this program; if not, write to the Free Software
%  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

[xrow,xcol] = size(x);
if (nargin < 1),
 error('At least 1 parameter is required');
elseif nargin<=2,
 N=xrow;
end;

hlength=floor(N/4);
if (rem(hlength,2)==0),
 hlength=hlength+1;
end;

if (nargin == 1),
 t=1:xrow; h = tftb_window(hlength); trace=0;
elseif (nargin == 2 | nargin == 3),
 h = tftb_window(hlength); trace = 0;
elseif (nargin == 4),
 trace = 0;
end;

if (N<0),
 error('N must be greater than zero');
end;
[trow,tcol] = size(t);
if (xcol==0)|(xcol>2),
 error('X must have one or two columns');
elseif (trow~=1),
 error('T must only have one row');
elseif (2^nextpow2(N)~=N),
 fprintf('For a faster computation, N should be a power of two\n');
end;

[hrow,hcol]=size(h); Lh=(hrow-1)/2; h=h/h(Lh+1);
if (hcol~=1)|(rem(hrow,2)==0),
 error('H must be a smoothing window with odd length');
end;

tfr= zeros (N,tcol) ;
if trace, disp('Pseudo Page distribution'); end;
for icol=1:tcol,
 ti= t(icol); tau=0:min([N-1,Lh,ti-1]);
 indices= rem(N+tau,N)+1;
 if trace, disprog(icol,tcol,10); end;
 tfr(indices,icol)=h(Lh+1+tau).*x(ti,1).*conj(x(ti-tau,xcol));
end;
if trace, fprintf('\n'); end;
tfr= real(fft(tfr));

if (nargout==0),
 tfrqview(tfr,x,t,'tfrppage',h);
elseif (nargout==3),
 if rem(N,2)==0,
  f=[0:N/2-1 -N/2:-1]'/N;
 else
  f=[0:(N-1)/2 -(N-1)/2:-1]'/N;
 end;
end;


function value = r8_choose ( n, k )

%*****************************************************************************80
%
%% R8_CHOOSE computes the binomial coefficient C(N,K).
%
%  Discussion:
%
%    The value is calculated in such a way as to avoid overflow and
%    roundoff.  The calculation is done in integer arithmetic.
%
%    The formula used is:
%
%      C(N,K) = N! / ( K! * (N-K)! )
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    31 March
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    ML Wolfson, HV Wright,
%    Algorithm 160:
%    Combinatorial of M Things Taken N at a Time,
%    Communications of the ACM,
%    Volume 6, Number 4, April 1963, page 161.
%
%  Parameters:
%
%    Input, integer N, K, are the values of N and K.
%
%    Output, real VALUE, the number of combinations of N
%    things taken K at a time.
%
  mn = min ( k, n - k );

  if ( mn < 0 )

    value = 0;

  elseif ( mn == 0 )

    value = 1;

  else

    mx = max ( k, n - k );
    value = mx + 1;

    for i = 2 : mn
      value = ( value * ( mx + i ) ) / i;
    end

  end

  return
end


function y = loggausspdf2(X, sigma)
% log pdf of Gaussian with zero mena
% Based on code written by Mo Chen (mochen@ie.cuhk.edu.hk). March 2009.
d = size(X,1);

[R,p]= chol(sigma);
if p ~= 0
    error('ERROR: sigma is not SPD.');
end
q = sum((R'\X).^2,1);  % quadratic term (M distance)
c = d*log(2*pi)+2*sum(log(diag(R)));   % normalization constant
y = -(c+q)/2;


function result = polygon_1_2d ( n, v )

%*****************************************************************************80
%
%% POLYGON_1_2D integrates the function 1 over a polygon in 2D.
%
%  Discussion
%
%    The polygon is bounded by the points (X(1:N), Y(1:N)).
%
%    INTEGRAL = 0.5 * SUM ( 1 <= I <= N )
%      ( X(I) + X(I-1) ) * ( Y(I) - Y(I-1) )
%
%    where X(0) and Y(0) should be replaced by X(N) and Y(N).
%
%    Note that the integral of 1 over a polygon is the area of the polygon.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    22 May 2005
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    S F Bockman,
%    Generalizing the Formula for Areas of Polygons to Moments,
%    American Mathematical Society Monthly,
%    1989, pages 131-132.
%
%  Parameters:
%
%    Input, integer N, the number of vertices of the polygon.
%    N should be at least 3 for a nonzero result.
%
%    Input, real V(2,N), the coordinates of the vertices
%    of the polygon.  These vertices should be given in counter-clockwise order.
%
%    Output, real RESULT, the value of the integral.
%
  result = 0.0;

  if ( n < 3 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'POLYGON_1_2D - Warning!\n' );
    fprintf ( 1, '  The number of vertices must be at least 3.\n' );
    fprintf ( 1, '  The input value of N = %d\n', n );
    error ( 'POLYGON_1_2D - Fatal error!' );
  end

  for i = 1 : n

    if ( i == 1 )
      im1 = n;
    else
      im1 = i - 1;
    end

    result = result + 0.5 * ( v(1,i) + v(1,im1) ) * ( v(2,i) - v(2,im1) );

  end

  return
end


function [V,F]=platonic_solid(varargin)

% function [V,F]=platonic_solid(n,r)
% ------------------------------------------------------------------------
% PLATONIC_SOLID Creates the PATCH data, the vertices (V) and faces (F) for
% a given platonic solid (according to "n" see below) with radius (r)
%
% n=1 -> Tetrahedron
% n=2 -> Cube
% n=3 -> Octahedron
% n=4 -> Icosahedron
% n=5 -> Dodecahedron
%
%
% Kevin Mattheus Moerman
% gibbon.toolbox@gmail.com
%
% 12/08/2014 Updated for GIBBON
%------------------------------------------------------------------------
%% Parse input

switch nargin
    case 1
        n=varargin{1};
        r=1;
    case 2
        n=varargin{1};
        r=varargin{2};
end

if isempty(r)
    r=1;
end

%%
switch n
    case 1 % Tetrahedron
        X=[-0.5;0.5;0;0;];
        Y=[-sqrt(3)/6;  -sqrt(3)/6; sqrt(3)/3; 0];
        Z=[-0.25.*sqrt(2/3); -0.25.*sqrt(2/3); -0.25.*sqrt(2/3);  0.75.*sqrt(2/3)];
        X=X([1 2 4 3]);
        Y=Y([1 2 4 3]);
        Z=Z([1 2 4 3]);
        F=[3,2,1;1,2,4;2,3,4;4,3,1;];
        F=fliplr(F);
    case 2 % Cube
        X=[-1;  1; 1; -1; -1;  1; 1; -1;];
        Y=[-1; -1; 1;  1; -1; -1; 1;  1;];
        Z=[-1; -1;-1; -1;  1;  1; 1;  1;];
        F=[4,3,2,1;
            1,2,6,5;
            2,3,7,6;
            3,4,8,7;
            4,1,5,8;
            5,6,7,8;];
    case 3 % Octahedron
        X=[-1;  1; 1; -1;  0;   0;];
        Y=[-1; -1; 1;  1;  0;   0;];
        Z=[ 0;   0; 0;  0; -1;  1;];
        F=[5,2,1;5,3,2;5,4,3;5,1,4;1,2,6;2,3,6;3,4,6;4,1,6;];
    case 4 % Icosahedron
        phi=(1+sqrt(5))/2;
        X=[0;0;0;0;-1;-1;1;1;-phi;phi;phi;-phi;];
        Y=[-1;-1;1;1;-phi;phi;phi;-phi;0;0;0;0;];
        Z=[-phi;phi;phi;-phi;0;0;0;0;-1;-1;1;1;];
        F=[9,4,1;1,5,9;1,8,5;10,8,1;4,10,1;5,2,12;12,2,3;12,3,6;12,6,9;12,9,5;10,7,11;8,10,11;2,8,11;3,2,11;7,3,11;2,5,8;10,4,7;7,6,3;6,7,4;6,4,9;];
    case 5 % Dodecahedron
        phi=(1+sqrt(5))/2;
        X=[1;(1/phi);-phi;phi;-1;0;-phi;1;-1;-1;1;(1/phi);-1;0;0;-(1/phi);phi;-(1/phi);1;0;];
        Y=[1;0;-(1/phi);(1/phi);1;-phi;(1/phi);-1;1;-1;-1;0;-1;-phi;phi;0;-(1/phi);0;1;phi;];
        Z=[1;phi;0;0;-1;-(1/phi);0;1;1;1;-1;-phi;-1;(1/phi);-(1/phi);phi;0;-phi;-1;(1/phi);];
        F=[20,9,16,2,1;2,16,10,14,8;16,9,7,3,10;7,9,20,15,5;18,13,3,7,5;3,13,6,14,10;6,13,18,12,11;6,11,17,8,14;11,12,19,4,17;1,2,8,17,4;1,4,19,15,20;12,18,5,15,19;];
    otherwise
        warning('False input for n')
end

%Altering radius
[THETA,PHI,~]=cart2sph(X,Y,Z);
[X,Y,Z]=sph2cart(THETA,PHI,r.*ones(size(X)));
V=[X(:) Y(:) Z(:)];

%%
% _*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 krn = spm_smoothkern(fwhm,x,t)
% Generate a Gaussian smoothing kernel
% FORMAT krn = spm_smoothkern(fwhm,x,t)
% fwhm - full width at half maximum
% x    - position
% t    - either 0 (nearest neighbour) or 1 (linear).
%        [Default: 1]
%
% krn  - value of kernel at position x
%__________________________________________________________________________
%
% For smoothing images, one should really convolve a Gaussian with a sinc
% function. For smoothing histograms, the kernel should be a Gaussian
% convolved with the histogram basis function used. This function returns
% a Gaussian convolved with a triangular (1st degree B-spline) basis
% function (by default). A Gaussian convolved with a hat function (0th
% degree B-spline) can also be returned.
%__________________________________________________________________________
% Copyright (C) 2005-2011 Wellcome Trust Centre for Neuroimaging

% John Ashburner
% $Id: spm_smoothkern.m 4419 2011-08-03 18:42:35Z guillaume $

if nargin<3, t = 1; end

% Variance from FWHM
s = (fwhm/sqrt(8*log(2)))^2+eps;

% The simple way to do it. Not good for small FWHM
% krn = (1/sqrt(2*pi*s))*exp(-(x.^2)/(2*s));

if t==0
    % Gaussian convolved with 0th degree B-spline
    % int(exp(-((x+t))^2/(2*s))/sqrt(2*pi*s),t= -0.5..0.5)
    w1  = 1/sqrt(2*s);
    krn = 0.5*(erf(w1*(x+0.5))-erf(w1*(x-0.5)));
    krn(krn<0) = 0;

elseif t==1
    % Gaussian convolved with 1st degree B-spline
    %  int((1-t)*exp(-((x+t))^2/(2*s))/sqrt(2*pi*s),t= 0..1)
    % +int((t+1)*exp(-((x+t))^2/(2*s))/sqrt(2*pi*s),t=-1..0)
    w1  =  0.5*sqrt(2/s);
    w2  = -0.5/s;
    w3  = sqrt(s/2/pi);
    krn = 0.5*(erf(w1*(x+1)).*(x+1) + erf(w1*(x-1)).*(x-1) - 2*erf(w1*x   ).* x)...
          +w3*(exp(w2*(x+1).^2)     + exp(w2*(x-1).^2)     - 2*exp(w2*x.^2));
    krn(krn<0) = 0;

else
    error('Only defined for nearest neighbour and linear interpolation.');
    % If anyone knows a nice formula for a sinc function convolved with a
    % a Gaussian, then that could be quite useful.
end


function variance = logistic_variance ( a, b )

%*****************************************************************************80
%
%% LOGISTIC_VARIANCE returns the variance of the Logistic PDF.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    13 September 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, real A, B, the parameters of the PDF.
%    0.0 < B.
%
%    Output, real VARIANCE, the variance of the PDF.
%
  variance = ( pi * b )^2 / 3.0;

  return
end


function [ lambda, x ] = jacobi_iterate ( n, a )

%*****************************************************************************80
%
%% JACOBI_ITERATE applies the Jacobi eigenvalue iteration to a symmetric matrix.
%
%  Discussion:
%
%    I had to modify the code, in order to avoid cases where the
%    off-diagonal element was not exactly zero, but very very close.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    29 October 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the order of A.
%
%    Input, real A(N,N), a symmetric matrix.
%    On output, the matrix has been overwritten by an approximately
%    diagonal matrix, with the eigenvalues on the diagonal.
%
%    Output, real LAMBDA(N), the computed eigenvalues.
%
%    Output, real X(N,N), the computed eigenvector matrix.
%
  eps = 0.00001;
  maxit = 100;

  error_frobenius = r8mat_is_symmetric ( n, n, a );

  if ( eps < error_frobenius )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'JACOBI_ITERATE - Fatal error!\n' );
    fprintf ( 1, '  The input matrix is not symmetric.\n' );
    error ( 'JACOBI_ITERATE - Fatal error!' );
  end

  b(1:n,1:n) = a(1:n,1:n);

  norm_fro = r8mat_norm_fro ( n, n, b );

  x = identity ( n, n );

  for it = 1 : maxit

    for i = 1 : n
      for j = 1 : i - 1

        if ( eps * norm_fro < abs ( b(i,j) ) + abs ( b(j,i) ) )

          u = ( b(j,j) - b(i,i) ) / ( b(i,j) + b(j,i) );

          t = r8_sign ( u ) / ( abs ( u ) + sqrt ( u * u + 1.0 ) );
          c = 1.0 / sqrt ( t * t + 1.0 );
          s = t * c;
%
%  A -> A * Q.
%
          for k = 1 : n
            t1 = b(i,k);
            t2 = b(j,k);
            b(i,k) = t1 * c - t2 * s;
            b(j,k) = t1 * s + t2 * c;
          end
%
%  A -> Q' * A
%
          for k = 1 : n
            t1 = b(k,i);
            t2 = b(k,j);
            b(k,i) = c * t1 - s * t2;
            b(k,j) = s * t1 + c * t2;
          end
%
%  X -> Q' * X
%
          for k = 1 : n
            t1 = x(k,i);
            t2 = x(k,j);
            x(k,i) = c * t1 - s * t2;
            x(k,j) = s * t1 + c * t2;
          end

        end

      end
    end
%
%  Test the size of the off-diagonal elements.
%
    sum2 = 0.0;
    for i = 1 : n
      sum2 = sum2 + sum ( abs ( b(i,1:i-1) ) );
    end

    if ( sum2 <= eps * ( norm_fro + 1.0 ) )
      break
    end

  end

  lambda = diag ( b );

  return
end


function [EC,ec,degij] = edge_nei_overlap_bu(CIJ)
% EDGE_NEI_OVERLAP_BU        Overlap amongst neighbors of two adjacent nodes
%
%   [EC,ec,degij] = edge_nei_bu(CIJ);
%
%   This function determines the neighbors of two nodes that are linked by
%   an edge, and then computes their overlap.  Connection matrix must be
%   binary and directed.  Entries of 'EC' that are 'inf' indicate that no
%   edge is present.  Entries of 'EC' that are 0 denote "local bridges", i.e.
%   edges that link completely non-overlapping neighborhoods.  Low values
%   of EC indicate edges that are "weak ties".
%
%   If CIJ is weighted, the weights are ignored.
%
%   Inputs:     CIJ,    undirected (binary/weighted) connection matrix
%
%   Outputs:    EC,     edge neighborhood overlap matrix
%               ec,     edge neighborhood overlap per edge, in vector format
%               degij,  degrees of node pairs connected by each edge
%
%   Reference: Easley and Kleinberg (2010) Networks, Crowds, and Markets.
%              Cambridge University Press, Chapter 3.
%
%   Olaf Sporns, Indiana University, 2012

[ik,jk,ck] = find(CIJ);
lel = length(ck);
N = size(CIJ,1);

[deg] = degrees_und(CIJ);

ec = zeros(1,lel);
degij = zeros(2,lel);
for e=1:lel
    neiik = setdiff(union(find(CIJ(ik(e),:)),find(CIJ(:,ik(e))')),[ik(e) jk(e)]);
    neijk = setdiff(union(find(CIJ(jk(e),:)),find(CIJ(:,jk(e))')),[ik(e) jk(e)]);
    ec(e) = length(intersect(neiik,neijk))/length(union(neiik,neijk));
    degij(:,e) = [deg(ik(e)) deg(jk(e))];
end;

ff = find(CIJ);
EC = 1./zeros(N);
EC(ff) = ec;                        %#ok<FNDSB>


function quadmom_test08 ( )

%*****************************************************************************80
%
%% QUADMOM_TEST08 integrates sin(x) against a lower truncated normal weight.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    05 October 2013
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Gene Golub, John Welsch,
%    Calculation of Gaussian Quadrature Rules,
%    Mathematics of Computation,
%    Volume 23, Number 106, April 1969, pages 221-230.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'QUADMOM_TEST08:\n' );
  fprintf ( 1, '  Integrate sin(x) against a lower truncated normal weight.\n' );

  mu = 0.0;
  sigma = 1.0;
  a = -3.0;

  fprintf ( 1, '\n' );
  fprintf ( 1, '  MU = %g\n', mu );
  fprintf ( 1, '  SIGMA = %g\n', sigma );
  fprintf ( 1, '  A = %g\n', a );
  fprintf ( 1, '\n' );
  fprintf ( 1, '   N    Estimate\n' );
  fprintf ( 1, '\n' );
%
%  N is the order of the quadrature rule.
%
  for n = 1 : 9
%
%  Compute the M = 2 * N + 1 moments.
%
    m = 2 * n + 1;

    moment = moments_truncated_normal_a ( m, mu, sigma, a );
%
%  Compute the N points and weights by the method of moments.
%
    [ x, w ] = moment_method ( n, moment );
%
%  Evaluate the quadrature rule when f(x) = sin(x).
%
    q = w' * sin ( x );

    fprintf ( 1, '  %2d  %14.6g\n', n, q );

  end

  return
end


function p = legendre(n,var)
%LEGENDRE     Legendre polynomial
%
%   p = legendre(n,var)
%
%Computed by recursion
%  p(0,x) = 1
%  p(1,x) = x
%  p(n,x) = (2*n-1)/n * x * p(n-1,x) - (n-1)/n * p(n-2,x)    for n>1
%
%Parameter var for dependent variable optional (default 'x')
%

% written  07/30/02     S.M. Rump
% modified 04/04/04     S.M. Rump  set round to nearest for safety
% modified 04/06/05     S.M. Rump  rounding unchanged
% modified 09/28/08     S.M. Rump  check for rounding to nearest improved
%

  e = 1e-30;
  if 1+e==1-e                           % fast check for rounding to nearest
    rndold = 0;
  else
    rndold = getround;
    setround(0)
  end

  if nargin==1
    var = 'x';
  else
    if ~ischar(var)
      error('variable must be string')
    end
  end

  if n==0
    p = polynom(1,var);
  elseif n==1
    p = polynom([1 0],var);
  else
    t1 = [1 zeros(1,n)];
    t2 = [0 1 zeros(1,n-1)];
    for i=3:n+1
      t3 = (2*i-3)/(i-1)*[0 t2(1:n)] - (i-2)/(i-1)*t1;
      t1 = t2;
      t2 = t3;
    end
    p = polynom(fliplr(t3),var);
  end

  if rndold
    setround(rndold)
  end


function [A, B] = zeroKronApprox( K, m, n )
%
%       [A, B] = zeroKronApprox( K, m, n );
%
% computes a kronecker sum approximation to the blurring matrix K
% that arises from the input PSF under zero boundary conditions.
% This approximation is done a-la Kamm-Nagy (see paper for details).
%
%  Input:
%         K - psfMatrix object
%         m - size of matrix A (assumed square)
%         n - size of matrix B (assumed square)
%
%  Output:
%      Matrices A and B such that K \aprrox A \otimes B.
%

%  J. Nagy  2/11/02

%  Modifications:  This used to be zeroSepMatrix, which did not compute
%                  A and B.
%  5/25/02, J. Nagy
%           This now computes A and B.
%
%  11/17/02, J. Nagy
%            This was designed for image processing problems, where
%            it is common to use lexicographical (row) ordering.
%            But @kronMatrix functions where designed using vec (column)
%            ordering.  This inconsistency has been fixed.

P1 = K.psf;
P2 = P1.image;
PSF = P2{1};
c1 = P1.center;
center = c1{1};

[mp, np] = size(PSF);

if ( mp ~= np )
  error('For now, we expect PSF to be square')
end

%
% Compute weighted PSF.
%
for i = 1:center(1)
  Aweights(i,1) = sqrt( i+mp-center(1) );
end;
for i = center(1)+1:mp
  Aweights(i,1) = sqrt( mp+center(1)-i);
end;
for i = 1:center(2)
  Bweights(i,1) = sqrt( i+np-center(2) );
end;
for i = center(2)+1:np
  Bweights(i,1) = sqrt( np+center(2)-i );
end;

Phat = (Aweights*Bweights').*PSF;
%
% Compute SVD of weighted PSF, which is then used to construct
% the separable approximation.
%
[U,S,V] = svd( Phat );

%
% check to make sure first column looks like
% a Gaussian, and is not inverted.
%
minU = abs(min(min(U(:,1))));
maxU = max(max(abs(U(:,1))));
if minU == maxU
  U = -U;
  V = -V;
end

%
% Construct approximation.
%
a = ( U(:,1) * sqrt(S(1,1)) ) ./ Aweights;
b = ( V(:,1) * sqrt(S(1,1)) ) ./ Bweights;

%
%  This construction corresponds to lexicographical ordering,
%  but kronMatrix does everything corresponding to vec ordering.
%  Thus, these two do not work together.
%%
%%A = build_toep(a, center(1), n);
%%B = build_toep(b, center(2), m);

%  To fix this, we just need to switch A and B.  Then kronMatrix
%  can continue to be used corresponding to a vec ordering.
%
A = build_toep(b, center(2), m);
B = build_toep(a, center(1), n);


function [ v1, v2, v3, v4 ] = tetrahedron_ref ( )

%*****************************************************************************80
%
%% TETRAHEDRON_REF returns the vertices of the reference tetrahedron.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 July 2014
%
%  Author:
%
%    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:
%
%    Output, real V1(3), V2(3), V3(3), V4(3), the vertices.
%
  v1(1) = - 1.0;
  v1(2) = - 1.0 / sqrt ( 3.0 );
  v1(3) = - 1.0 / sqrt ( 6.0 );

  v2(1) =   0.0;
  v2(2) =   2.0 / sqrt ( 3.0 );
  v2(3) = - 1.0 / sqrt ( 6.0 );

  v3(1) =   1.0;
  v3(2) = - 1.0 / sqrt ( 3.0 );
  v3(3) = - 1.0 / sqrt ( 6.0 );

  v4(1) =   0.0;
  v4(2) =   0.0;
  v4(3) =   3.0 / sqrt ( 6.0 );

  return
end


function pdf = beta_pdf ( x, a, b )

%*****************************************************************************80
%
%% BETA_PDF evaluates the Beta PDF.
%
%  Discussion:
%
%    PDF(X)(A,B) = X**(A-1) * (1-X)**(B-1) / BETA(A,B).
%
%    A = B = 1 yields the Uniform distribution on [0,1].
%    A = B = 1/2 yields the Arcsin distribution.
%        B = 1 yields the power function distribution.
%    A = B -> Infinity tends to the Normal distribution.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    03 September 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, real X, the argument of the PDF.
%    0.0 <= X <= 1.0.
%
%    Input, real A, B, the parameters of the PDF.
%    0.0 < A,
%    0.0 < B.
%
%    Output, real PDF, the value of the PDF.
%
  if ( x < 0.0 | 1.0 < x )
    pdf = 0.0;
  else
    pdf = x^( a - 1.0 ) * ( 1.0 - x )^( b - 1.0 ) / beta ( a, b );
  end

  return
end


%% A script to generate copy'able functions for quad-curl problem
% used in "Error analysis of a decoupled finite element method for quad-curl problems"
% https://arxiv.org/abs/2102.03396
%
%
% Copyright (C) Long Chen. See COPYRIGHT.txt for details.

clear; clc;
syms x y z
% U = [sin(y), -sin(z), sin(x)];
% U = [0, 0, (sin(x))^2*(sin(y))^2*sin(z)];
U = [0, 0, (sin(x)).^3.*(sin(y)).^3.*(sin(z)).^2];
% U = [0, 0, x^2*(1-x)^2*y^2*(1-y)^2*z^2*(1-z)^2];

X = [x y z];

%%
curlu = curl(U,X);
curlcurlu = curl(curlu,X);
tricurlu = curl(curlcurlu,X);
quadcurlu = curl(tricurlu,X);
pentacurlu = curl(quadcurlu,X);
hexacurlu = curl(pentacurlu,X);

vectorsStr = {'curlu', 'curlcurlu', 'tricurlu', ...
              'quadcurlu', 'pentacurlu', 'hexacurlu'};

%%
for idx = 1:length(vectorsStr)
    vector = eval(vectorsStr{idx});
    fprintf("function s = %s(p)\n",vectorsStr{idx})
    fprintf("x = p(:,1); y = p(:,2); z = p(:,3);\n")
    for j = 1:3
        vStr = char(vector(j));
        vStr = strrep(vStr, '*', '.*');
        vStr = strrep(vStr, '^', '.^');
        fprintf("s(:,%d) = %s;\n", j, vStr)
    end
    fprintf('end\n\n')
end


function [costs,paths] = dijkstra(AorV,xyCorE,SID,FID,iswaitbar)
%DIJKSTRA Calculate Minimum Costs and Paths using Dijkstra's Algorithm
%
%   Inputs:
%     [AorV] Either A or V where
%         A   is a NxN adjacency matrix, where A(I,J) is nonzero (=1)
%               if and only if an edge connects point I to point J
%               NOTE: Works for both symmetric and asymmetric A
%         V   is a Nx2 (or Nx3) matrix of x,y,(z) coordinates
%     [xyCorE] Either xy or C or E (or E3) where
%         xy  is a Nx2 (or Nx3) matrix of x,y,(z) coordinates (equivalent to V)
%               NOTE: only valid with A as the first input
%         C   is a NxN cost (perhaps distance) matrix, where C(I,J) contains
%               the value of the cost to move from point I to point J
%               NOTE: only valid with A as the first input
%         E   is a Px2 matrix containing a list of edge connections
%               NOTE: only valid with V as the first input
%         E3  is a Px3 matrix containing a list of edge connections in the
%               first two columns and edge weights in the third column
%               NOTE: only valid with V as the first input
%     [SID] (optional) 1xL vector of starting points
%         if unspecified, the algorithm will calculate the minimal path from
%         all N points to the finish point(s) (automatically sets SID = 1:N)
%     [FID] (optional) 1xM vector of finish points
%         if unspecified, the algorithm will calculate the minimal path from
%         the starting point(s) to all N points (automatically sets FID = 1:N)
%     [iswaitbar] (optional) a scalar logical that initializes a waitbar if nonzero
%
%   Outputs:
%     [costs] is an LxM matrix of minimum cost values for the minimal paths
%     [paths] is an LxM cell array containing the shortest path arrays
%
%   Revision Notes:
%     (4/29/09) Previously, this code ignored edges that have a cost of zero,
%     potentially producing an incorrect result when such a condition exists.
%     I have solved this issue by using NaNs in the table rather than a
%     sparse matrix of zeros. However, storing all of the NaNs requires more
%     memory than a sparse matrix. This may be an issue for massive data
%     sets, but only if there are one or more 0-cost edges, because a sparse
%     matrix is still used if all of the costs are positive.
%
%   Note:
%     If the inputs are [A,xy] or [V,E], the cost is assumed to be (and is
%       calculated as) the point-to-point Euclidean distance
%     If the inputs are [A,C] or [V,E3], the cost is obtained from either
%       the C matrix or from the edge weights in the 3rd column of E3
%
%   Example:
%       % Calculate the (all pairs) shortest distances and paths using [A,xy] inputs
%       n = 7; A = zeros(n); xy = 10*rand(n,2)
%       tri = delaunay(xy(:,1),xy(:,2));
%       I = tri(:); J = tri(:,[2 3 1]); J = J(:);
%       IJ = I + n*(J-1); A(IJ) = 1
%       [costs,paths] = dijkstra(A,xy)
%
%   Example:
%       % Calculate the (all pairs) shortest distances and paths using [A,C] inputs
%       n = 7; A = zeros(n); xy = 10*rand(n,2)
%       tri = delaunay(xy(:,1),xy(:,2));
%       I = tri(:); J = tri(:,[2 3 1]); J = J(:);
%       IJ = I + n*(J-1); A(IJ) = 1
%       a = (1:n); b = a(ones(n,1),:);
%       C = round(reshape(sqrt(sum((xy(b,:) - xy(b',:)).^2,2)),n,n))
%       [costs,paths] = dijkstra(A,C)
%
%   Example:
%       % Calculate the (all pairs) shortest distances and paths using [V,E] inputs
%       n = 7; V = 10*rand(n,2)
%       I = delaunay(V(:,1),V(:,2));
%       J = I(:,[2 3 1]); E = [I(:) J(:)]
%       [costs,paths] = dijkstra(V,E)
%
%   Example:
%       % Calculate the (all pairs) shortest distances and paths using [V,E3] inputs
%       n = 7; V = 10*rand(n,2)
%       I = delaunay(V(:,1),V(:,2));
%       J = I(:,[2 3 1]);
%       D = sqrt(sum((V(I(:),:) - V(J(:),:)).^2,2));
%       E3 = [I(:) J(:) D]
%       [costs,paths] = dijkstra(V,E3)
%
%   Example:
%       % Calculate the shortest distances and paths from the 3rd point to all the rest
%       n = 7; V = 10*rand(n,2)
%       I = delaunay(V(:,1),V(:,2));
%       J = I(:,[2 3 1]); E = [I(:) J(:)]
%       [costs,paths] = dijkstra(V,E,3)
%
%   Example:
%       % Calculate the shortest distances and paths from all points to the 2nd
%       n = 7; A = zeros(n); xy = 10*rand(n,2)
%       tri = delaunay(xy(:,1),xy(:,2));
%       I = tri(:); J = tri(:,[2 3 1]); J = J(:);
%       IJ = I + n*(J-1); A(IJ) = 1
%       [costs,paths] = dijkstra(A,xy,1:n,2)
%
%   Example:
%       % Calculate the shortest distance and path from points [1 3 4] to [2 3 5 7]
%       n = 7; V = 10*rand(n,2)
%       I = delaunay(V(:,1),V(:,2));
%       J = I(:,[2 3 1]); E = [I(:) J(:)]
%       [costs,paths] = dijkstra(V,E,[1 3 4],[2 3 5 7])
%
%   Example:
%       % Calculate the shortest distance and path between two points
%       n = 1000; A = zeros(n); xy = 10*rand(n,2);
%       tri = delaunay(xy(:,1),xy(:,2));
%       I = tri(:); J = tri(:,[2 3 1]); J = J(:);
%       D = sqrt(sum((xy(I,:)-xy(J,:)).^2,2));
%       I(D > 0.75,:) = []; J(D > 0.75,:) = [];
%       IJ = I + n*(J-1); A(IJ) = 1;
%       [cost,path] = dijkstra(A,xy,1,n)
%       gplot(A,xy,'k.:'); hold on;
%       plot(xy(path,1),xy(path,2),'ro-','LineWidth',2); hold off
%       title(sprintf('Distance from 1 to 1000 = %1.3f',cost))
%
% Web Resources:
%   Dijkstra's Algorithm
%   Graphs
%   Adjacency Matrix
%
% See also: gplot, gplotd, gplotdc, distmat, ve2axy, axy2ve
%
% Author: Joseph Kirk
% Email: jdkirk630@gmail.com
% Release: 1.1
% Date: 4/29/09

% Process Inputs
error(nargchk(2,5,nargin));
all_positive = 1;
[n,nc] = size(AorV);
[m,mc] = size(xyCorE);
[E,cost] = processInputs(AorV,xyCorE);
if nargin < 5
    iswaitbar = 0;
end
if nargin < 4
    FID = (1:n);
end
if nargin < 3
    SID = (1:n);
end
if max(SID) > n || min(SID) < 1
    eval(['help ' mfilename]);
    error('Invalid [SID] input. See help notes above.');
end
if max(FID) > n || min(FID) < 1
    eval(['help ' mfilename]);
    error('Invalid [FID] input. See help notes above.');
end

isreversed = 0;
if length(FID) < length(SID)
    E = E(:,[2 1]);
    cost = cost';
    tmp = SID;
    SID = FID;
    FID = tmp;
    isreversed = 1;
end

L = length(SID);
M = length(FID);
costs = zeros(L,M);
paths = num2cell(nan(L,M));

% Find the Minimum Costs and Paths using Dijkstra's Algorithm
if iswaitbar, wbh = waitbar(0,'Please Wait ... '); end
for k = 1:L
    % Initializations
    if all_positive, TBL = sparse(1,n); else TBL = NaN(1,n); end
    min_cost = Inf(1,n);
    settled = zeros(1,n);
    path = num2cell(nan(1,n));
    I = SID(k);
    min_cost(I) = 0;
    TBL(I) = 0;
    settled(I) = 1;
    path(I) = {I};

    while any(~settled(FID))
        % Update the Table
        TAB = TBL;
        if all_positive, TBL(I) = 0; else TBL(I) = NaN; end
        nids = find(E(:,1) == I);
        % Calculate the Costs to the Neighbor Points and Record Paths
        for kk = 1:length(nids)
            J = E(nids(kk),2);
            if ~settled(J)
                c = cost(I,J);
                if all_positive, empty = ~TAB(J); else empty = isnan(TAB(J)); end
                if empty || (TAB(J) > (TAB(I) + c))
                    TBL(J) = TAB(I) + c;
                    if isreversed
                        path{J} = [J path{I}];
                    else
                        path{J} = [path{I} J];
                    end
                else
                    TBL(J) = TAB(J);
                end
            end
        end

        if all_positive, K = find(TBL); else K = find(~isnan(TBL)); end
        % Find the Minimum Value in the Table
        N = find(TBL(K) == min(TBL(K)));
        if isempty(N)
            break
        else
            % Settle the Minimum Value
            I = K(N(1));
            min_cost(I) = TBL(I);
            settled(I) = 1;
        end
    end
    % Store Costs and Paths
    costs(k,:) = min_cost(FID);
    paths(k,:) = path(FID);
    if iswaitbar, waitbar(k/L,wbh); end
end
if iswaitbar, close(wbh); end

if isreversed
    costs = costs';
    paths = paths';
end

if L == 1 && M == 1
    paths = paths{1};
end

% -------------------------------------------------------------------
    function [E,C] = processInputs(AorV,xyCorE)
        C = sparse(n,n);
        if n == nc
            if m == n
                if m == mc % Inputs: A,cost
                    A = AorV;
                    A = A - diag(diag(A));
                    C = xyCorE;
                    all_positive = all(C(logical(A)) > 0);
                    E = a2e(A);
                else % Inputs: A,xy
                    A = AorV;
                    A = A - diag(diag(A));
                    xy = xyCorE;
                    E = a2e(A);
                    D = ve2d(xy,E);
                    all_positive = all(D > 0);
                    for row = 1:length(D)
                        C(E(row,1),E(row,2)) = D(row);
                    end
                end
            else
                eval(['help ' mfilename]);
                error('Invalid [A,xy] or [A,cost] inputs. See help notes above.');
            end
        else
            if mc == 2 % Inputs: V,E
                V = AorV;
                E = xyCorE;
                D = ve2d(V,E);
                all_positive = all(D > 0);
                for row = 1:m
                    C(E(row,1),E(row,2)) = D(row);
                end
            elseif mc == 3 % Inputs: V,E3
                E3 = xyCorE;
                all_positive = all(E3 > 0);
                E = E3(:,1:2);
                for row = 1:m
                    C(E3(row,1),E3(row,2)) = E3(row,3);
                end
            else
                eval(['help ' mfilename]);
                error('Invalid [V,E] inputs. See help notes above.');
            end
        end
    end

    % Convert Adjacency Matrix to Edge List
    function E = a2e(A)
        [I,J] = find(A);
        E = [I J];
    end

    % Compute Euclidean Distance for Edges
    function D = ve2d(V,E)
        VI = V(E(:,1),:);
        VJ = V(E(:,2),:);
        D = sqrt(sum((VI - VJ).^2,2));
    end
end