function title = p15_title ( )

%*****************************************************************************80
%
%% P15_TITLE returns the title for problem 15.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    29 August 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, string TITLE, the title of the problem.
%
  title = 'log(x) / ( 1 + 100 x^2 )';

  return
end


% StackOverflow Q2080835
% https://stackoverflow.com/questions/2080835
% Deriving the Inverse Filter of Image Convolution Kernel
% References:
%   1.  A
% Remarks:
%   1.  B
% TODO:
% 	1.  C
% Release Notes
% - 1.0.000     14/01/2019
%   *   First release.

%% General Parameters

subStreamNumberDefault = 0;

run('InitScript.m');

figureIdx           = 0; %<! Continue from Question 1
figureCounterSpec   = '%04d';

generateFigures = OFF;
generateImages  = OFF;

OPERATION_MODE_CONVOLUTION = 1;
OPERATION_MODE_CORRELATION = 2;

CONVOLUTION_SHAPE_FULL         = 1;
CONVOLUTION_SHAPE_SAME         = 2;
CONVOLUTION_SHAPE_VALID        = 3;

%% Simulation Parameters

operationMode = OPERATION_MODE_CONVOLUTION;
convShape = CONVOLUTION_SHAPE_VALID;

% The Input Kernel - F
numRowsF = 11;
numColsF = 7;

% The Inverse Kernel - G
numRowsG = 201;
numColsG = 201;

numIteraions    = 50000;
stepSize        = 5e-5;

%% Generate Data

numRowsH = numRowsF + numRowsG - 1;
numColsH = numColsF + numColsG - 1;

mF = rand(numRowsF, numColsF);
mG = ones(numRowsG, numColsG); %<! Initial condition

%% Verify Gradient
% Using Random h Filter

mH = rand(numRowsH, numColsH);

hObjFun = @(mG) 0.5 * sum((conv2(mF, mG, 'full') - mH) .^ 2, 'all');

mObjFunGrad = conv2(conv2(mF, mG, 'full') - mH, mF(end:-1:1, end:-1:1), 'valid');

mObjFunGradNum = zeros(size(mG));
mTmp = zeros(size(mObjFunGradNum));

derEps = 5e-6;

% Numeric Gradient
for ii = 1:numel(mObjFunGradNum)
    mTmp(ii)            = derEps;
    mObjFunGradNum(ii)  = (hObjFun(mG + mTmp) - hObjFun(mG)) / derEps;
    mTmp(ii)            = 0;
end

mE = mObjFunGradNum - mObjFunGrad;
gradError = max(abs(mE(:)));

%% Derive the Inverse

% The Target Kernel - Discrete Delta
mH = zeros(numRowsH, numColsH);
mH(ceil(numRowsH / 2), ceil(numColsH / 2)) = 1; %<! Delta

% Gradient Descent (Could improved with Accelerated Gradient Descent)
for ii = 1:numIteraions
    mObjFunGrad = conv2(conv2(mF, mG, 'full') - mH, mF(end:-1:1, end:-1:1), 'valid');
    mG          = mG - (stepSize * mObjFunGrad);
end

mA = conv2(mG, mF, 'full');
mE = abs(mA - mH);
inverseError = max(mE(:));

%% Analysis

disp(['Analytic Gradient vs. Numeric Gradient - Maximum Absolute Deviation - ', num2str(gradError)]);
disp(['Inverse Filter - Maximum Deviation - ', num2str(inverseError)]);

%% Display Results

figureIdx = figureIdx + 1;

hFigure = figure('Position', figPosLarge);
hAxes   = axes();
hImgObj = imshow(mA, []);
set(get(hAxes, 'Title'), 'String', {['Convolution Result']}, ...
    'FontSize', fontSizeTitle);

if(generateFigures == ON)
    saveas(hFigure,['Figure', num2str(figureIdx, figureCounterSpec), '.png']);
end

%% Restore Defaults

% set(0, 'DefaultFigureWindowStyle', 'normal');
% set(0, 'DefaultAxesLooseInset', defaultLoosInset);


function b = r8vm_to_r8ge ( m, n, a, b )

%*****************************************************************************80
%
%% R8VM_TO_R8GE copies a R8VM matrix to a R8GE matrix.
%
%  Discussion:
%
%    The R8VM storage format is used for an M by N Vandermonde matrix.
%    An M by N Vandermonde matrix is defined by the values in its second
%    row, which will be written here as X(1:N).  The matrix has a first
%    row of 1's, a second row equal to X(1:N), a third row whose entries
%    are the squares of the X values, up to the M-th row whose entries
%    are the (M-1)th powers of the X values.  The matrix can be stored
%    compactly by listing just the values X(1:N).
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    05 February 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, N, the number of rows and columns of the matrix.
%
%    Input, real A(N,1), the R8VM matrix.
%
%    Output, real B(M,N), the R8GE matrix.
%
  a = a(:);

  for i = 1 : m
    for j = 1 : n
      if ( i == 1 )
        b(i,j) = 1.0;
      else
        b(i,j) = b(i-1,j) * a(j,1);
      end
    end
  end

  return
end


function asa245_test03 ( )

%*****************************************************************************80
%
%% ASA245_TEST03 demonstrates the use of GAMMA_LN.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    25 September 2014
%
%  Author:
%
%    John Burkardt
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'ASA245_TEST03:\n' );
  fprintf ( 1, '  GAMMALN computes the logarithm of the\n' );
  fprintf ( 1, '  Gamma function.\n' );
  fprintf ( 1, '  GAMMALN is a builtin function in MATLAB.\n' );
  fprintf ( 1, '  We compare the result to tabulated values.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '          X                     ' );
  fprintf ( 1, 'FX                        FX2\n' );
  fprintf ( 1, '                                ' );
  fprintf ( 1, '(Tabulated)               (GAMMALN)                DIFF\n' );
  fprintf ( 1, '\n' );

  n_data = 0;

  while ( 1 )

    [ n_data, x, fx ] = gamma_log_values ( n_data );

    if ( n_data == 0 )
      break
    end

    fx2 = gammaln ( x );

    fprintf ( 1, '  %24.16f  %24.16e  %24.16e  %10.4e\n', ...
    x, fx, fx2, abs ( fx - fx2 ) );

  end

  return
end


function geometry_test02035 ( )

%*****************************************************************************80
%
%% TEST02035 tests CYLINDER_SAMPLE_3D.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    04 December 2010
%
%  Author:
%
%    John Burkardt
%
  n = 20;

  p1 = [ 0.0; -2.0; 0.0 ];
  p2 = [ 0.0;  2.0; 0.0 ];
  r = 1.0;
  seed = 123456789;

  fprintf ( 1, '\n' );
  fprintf ( 1, 'TEST02035\n' );
  fprintf ( 1, '  CYLINDER_SAMPLE_3D samples points in a cylinder.\n' );

  fprintf ( 1, '\n' );
  fprintf ( 1, '  Radius R = %f\n', r );
  fprintf ( 1, '  Center of bottom disk = %f  %f  %f\n', p1(1:3,1) );
  fprintf ( 1, '  Center of top disk =    %f  %f  %f\n', p2(1:3,1) );

  [ p, seed ] = cylinder_sample_3d ( p1, p2, r, n, seed );

  r8mat_transpose_print ( 3, n, p, '  Sample points:' );

  return
end


function [v_0_vec, v_vec, a, e, p] = LambertBattin_v2(r_0_vec, r_vec, delta_t, t_m, N)
%
% USAGE = [v_0_vec, v_vec, a, e, p] = LambertBattin_v2(r_0_vec, r_vec, delta_t, t_m, N)
%

% Function constants
% mu = 3.986e5;
mu = 398600.4418; % Sun gravitational constant (km^3/sec^2)
tol = 1e-6;
iter_max = 500;

r_0 = norm(r_0_vec);
r = norm(r_vec);

cos_delta_nu = dot(r_0_vec, r_vec) / (r_0 * r);
sin_delta_nu = t_m * sqrt(1 - cos_delta_nu^2);
delta_nu = atan2(sin_delta_nu, cos_delta_nu);
if delta_nu < 0

    delta_nu = 2 * pi + delta_nu;

end

c = sqrt(r_0^2 + r^2 - 2 * r_0 * r * cos_delta_nu);
s = (r_0 + r + c) / 2;

epsilon = (r - r_0) / r_0;
tan_sq_2w = (epsilon^2 / 4) / (sqrt(r / r_0) + (r / r_0) * (2 + sqrt(r / r_0)));
r_0p = sqrt(r_0 * r) * ((cos(delta_nu / 4))^2 + tan_sq_2w);

sin_sq_delta_nu_4 = (sin(delta_nu / 4))^2;
cos_sq_delta_nu_4 = (cos(delta_nu / 4))^2;
cos_delta_nu_2 = cos(delta_nu / 2);
if (delta_nu > 0) && (delta_nu < pi)

    l = (sin_sq_delta_nu_4 + tan_sq_2w) / (sin_sq_delta_nu_4 + tan_sq_2w + cos_delta_nu_2);

elseif (delta_nu > pi) && (delta_nu < 2*pi)

    l = (cos_sq_delta_nu_4 + tan_sq_2w - cos_delta_nu_2) / (cos_sq_delta_nu_4 + tan_sq_2w);

else

    error('Delta_nu out of range');

end

m = (mu * delta_t^2) / (8 * r_0p^3);

if N == 0

    % Initial estimate for x
    x_new = l;

    loop_flag = 1;
    i_iter = 1;
    while loop_flag == 1

        x_old = x_new;
        [x_new, y] = original_sub(x_old, l, m, N);

        if abs(x_new - x_old) < tol

            loop_flag = 0;

        elseif i_iter < iter_max

            i_iter = i_iter + 1;

        else

            loop_flag = 0;
            x_new = NaN;
            y = NaN;

        end

    end

    x_f = x_new;
    y_f = y;

else % N > 0

    loop_flag_R = 1;
    loop_flag_L = 1;
    i_iter_R = 1;
    i_iter_L = 1;

    % Initial estimate for x_R
    x_new = l;

    while loop_flag_R == 1

        x_old = x_new;
        [x_new, y] = reverse_sub(x_old, l, m, N);

        if y < 1

            % In divergent region for the reversed successive substitution
            % Begin original successive substitution to find x_L
            x_new = l;
            x_old = x_new;
            [x_new, y] = original_sub(x_old, l, m, N);

            while loop_flag_L == 1

                x_old = x_new;
                [x_new, y] = original_sub(x_old, l, m, N);

                if abs(x_new - x_old) < tol

                    loop_flag_L = 0;

                elseif i_iter_R < iter_max

                    i_iter_R = i_iter_R + 1;

                else

                    loop_flag_L = 0;
                    x_new = NaN;
                    y = NaN;

                end

            end

            y_L = y;
            x_L = x_new;

            % Reset initial guess for x_R
            x_new = x_new / 2;
            x_old = x_new - 2 * tol;

        end

        if abs(x_new - x_old) < tol

            loop_flag_R = 0;

        elseif i_iter_R < iter_max

            i_iter_R = i_iter_R + 1;

        else

            loop_flag_R = 0;
            x_new = NaN;
            y = NaN;

        end

    end

    y_R = y;
    x_R = x_new;

    % Only continue if x_L and y_L were not found in above iteration
    if loop_flag_L == 1

        % Initial estimate for x_L & y
        x_new = 2 * x_R;

        while loop_flag_L == 1

            x_old = x_new;
            [x_new, y] = original_sub(x_old, l, m, N);

            if abs(x_new - x_old) < tol

                loop_flag_L = 0;

            elseif i_iter_L < iter_max

                i_iter_L = i_iter_L + 1;

            else

                loop_flag_L = 0;
                x_new = NaN;
                y = NaN;

            end

        end

        y_L = y;
        x_L = x_new;

    end

    y_f = [y_R; y_L];
    x_f = [x_R; x_L];

end

if N == 0

    n = 1;

else

    n = 2;

end

a = zeros(n,1);
e = zeros(n,1);
p = zeros(n,1);
v_vec = zeros(n,3);
v_0_vec = zeros(n,3);
for i = 1:n

    a(i) = mu * delta_t^2 / (16 * r_0p^2 * x_f(i) * y_f(i)^2);
    e(i) = sqrt((epsilon^2 + 4 * (r / r_0) * (sin(delta_nu / 2))^2 * ((l - x_f(i)) / (l + x_f(i)))^2) / (epsilon^2 + 4 * (r / r_0) * (sin(delta_nu / 2))^2));
    p(i) = (4 * r_0p^2 * r_0 * r * y_f(i)^2 * (1 + x_f(i))^2 * (sin(delta_nu / 2))^2) / (mu * delta_t^2);
    if a(i) > 0

        f = 1 - (r / p(i)) * (1 - cos_delta_nu);
        g = (r * r_0 * sin_delta_nu) / sqrt(mu * p(i));
        g_dot = 1 - (r_0 / p(i)) * (1 - cos_delta_nu);

    else

        alpha_h = 2 * asinh(sqrt(s / (-2 * a(i))));
        beta_h = 2 * asinh(sqrt((s - c) / (-2 * a(i))));

        delta_H = alpha_h - beta_h;

        f = 1 - a(i) * (1 - cosh(delta_H)) / r;
        g = delta_t - sqrt(-a(i)^3 / mu) * (sinh(delta_H) - delta_H);
        g_dot = 1 - a(i) * (1 - cosh(delta_H)) / r;

    end

    v_0_vec(i,:) = (1 / g) .* (r_vec - f .* r_0_vec);
    v_vec(i,:) = (1 / g) .* (g_dot .* r_vec - r_0_vec);

end

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

function [y] = cubic_solv(c1, c2, c3)

P = c2 - c1^2 / 3;
Q = c3 + (2 * c1^3 - 9 * c1 * c2) / 27;
D = Q^2 / 4 + P^3 / 27;

T = (-Q / 2 + sqrt(D))^(1/3) + (-Q/2 - sqrt(D))^(1/3);
y = T - c1 / 3;

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

function [x_new, y] = original_sub(x_old, l, m, N)

E = 2 * atan(sqrt(x_old));
c = -m * (N * pi + E - sin(E)) / (4 * (tan(E / 2))^3);
y = cubic_solv(-1, 0, c);
x_new = sqrt(((1 - l) / 2)^2 + (m / y^2)) - ((1 + l) / 2);

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

function [x_new, y] = reverse_sub(x_old, l, m, N)

y = sqrt(m / ((l + x_old) * (1 + x_old)));
E_0 = 2 * atan(sqrt(x_old));
E = y2E(E_0, y, m, N);
x_new = (tan(E / 2))^2;

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

function [E] = y2E(E_0, y, m, N)

tol = 1e-6;
iter_max = 200;

q = 4 * (y^3 - y^2) / m;
h = (N * pi + E_0 - sin(E_0)) / (tan(E_0 / 2))^3 - q;

if h < 0

    loop_flag_h = 1;
    i_iter_h = 1;
    while loop_flag_h == 1

        E_0 = E_0 / 2;
        h = (N * pi + E_0 - sin(E_0)) / (tan(E_0 / 2))^3 - q;

        if h >= 0

            loop_flag_h = 0;

        elseif i_iter_h < iter_max

            i_iter_h = i_iter_h + 1;

        else

            loop_flag_h = 0;
            E_0 = NaN;

        end

    end

end

i_iter_E = 1;
loop_flag_E = 1;
while loop_flag_E == 1

    h = (N * pi + E_0 - sin(E_0)) / (tan(E_0 / 2))^3 - q;
    h_prime = (-3 * (cos(E_0 / 2))^2) * (N * pi + E_0 - sin(E_0)) / (2 * (sin(E_0 / 2))^4) + (1 - cos(E_0)) / (tan(E_0 / 2))^3;
    E = E_0 - h / h_prime;

    if abs(E - E_0) < tol

        loop_flag_E = 0;

    elseif i_iter_E < iter_max

        i_iter_E = i_iter_E + 1;
        E_0 = E;

    else

        loop_flag_E = 0;
        E = NaN;

    end

end


function Cba=quat2dcm(qba)

a=qba(1);
b=qba(2);
c=qba(3);
d=qba(4);

Cba=zeros(3);
Cba(1,1)=a^2+b^2-c^2-d^2;
Cba(2,1)=2*(b*c+a*d);
Cba(3,1)=2*(b*d-a*c);

Cba(1,2)=2*(b*c-a*d);
Cba(2,2)=a^2-b^2+c^2-d^2;
Cba(3,2)=2*(c*d+a*b);

Cba(1,3)=2*(b*d+a*c);
Cba(2,3)=2*(c*d-a*b);
Cba(3,3)=a^2-b^2-c^2+d^2;


function Lms=normapx(Ns, delta, epsilon)
K=sqrt(delta*(1-delta))*norminv(epsilon, 0, 1);
Lms = Ns.*(1-delta) + K.*sqrt(Ns);


function [tru] = computeTrueAnomFromHypAnom(HypA, ecc)
%computeTrueAnomFromHypAnom Summary of this function goes here
%   Detailed explanation goes here

    upper = sqrt(ecc+1) .* tanh(HypA/2);
    lower = sqrt(ecc-1);
    tru = atan2(upper, lower) * 2;
end


function [ n_data, x, fx ] = struve_h0_values ( n_data )

%*****************************************************************************80
%
%% STRUVE_H0_VALUES returns some values of the Struve H0 function.
%
%  Discussion:
%
%    The function is defined by:
%
%      HO(x) = 2/pi * Integral ( 0 <= t <= pi/2 ) sin ( x * cos ( t ) ) dt
%
%    In Mathematica, the function can be evaluated by:
%
%      StruveH[0,x]
%
%    The data was reported by McLeod.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    19 September 2004
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Milton Abramowitz and Irene Stegun,
%    Handbook of Mathematical Functions,
%    US Department of Commerce, 1964.
%
%    Allan McLeod,
%    Algorithm 757, MISCFUN: A software package to compute uncommon
%    special functions,
%    ACM Transactions on Mathematical Software,
%    Volume 22, Number 3, September 1996, pages 288-301.
%
%    Stephen Wolfram,
%    The Mathematica Book,
%    Fourth Edition,
%    Wolfram Media / Cambridge University Press, 1999.
%
%  Parameters:
%
%    Input/output, integer N_DATA.  The user sets N_DATA to 0 before the
%    first call.  On each call, the routine increments N_DATA by 1, and
%    returns the corresponding data; when there is no more data, the
%    output value of N_DATA will be 0 again.
%
%    Output, real X, the argument of the function.
%
%    Output, real FX, the value of the function.
%
  n_max = 20;

  fx_vec = [ ...
      0.12433974658847434366E-02, ...
     -0.49735582423748415045E-02, ...
      0.39771469054536941564E-01, ...
     -0.15805246001653314198E+00, ...
      0.56865662704828795099E+00, ...
      0.66598399314899916605E+00, ...
      0.79085884950809589255E+00, ...
     -0.13501457342248639716E+00, ...
      0.20086479668164503137E+00, ...
     -0.11142097800261991552E+00, ...
     -0.17026804865989885869E+00, ...
     -0.13544931808186467594E+00, ...
      0.94393698081323450897E-01, ...
     -0.10182482016001510271E+00, ...
      0.96098421554162110012E-01, ...
     -0.85337674826118998952E-01, ...
     -0.76882290637052720045E-01, ...
      0.47663833591418256339E-01, ...
     -0.70878751689647343204E-01, ...
      0.65752908073352785368E-01 ];

  x_vec = [ ...
        0.0019531250E+00, ...
       -0.0078125000E+00, ...
        0.0625000000E+00, ...
       -0.2500000000E+00, ...
        1.0000000000E+00, ...
        1.2500000000E+00, ...
        2.0000000000E+00, ...
       -4.0000000000E+00, ...
        7.5000000000E+00, ...
       11.0000000000E+00, ...
       11.5000000000E+00, ...
      -16.0000000000E+00, ...
       20.0000000000E+00, ...
       25.0000000000E+00, ...
      -30.0000000000E+00, ...
       50.0000000000E+00, ...
       75.0000000000E+00, ...
      -80.0000000000E+00, ...
      100.0000000000E+00, ...
     -125.0000000000E+00 ];

  if ( n_data < 0 )
    n_data = 0;
  end

  n_data = n_data + 1;

  if ( n_max < n_data )
    n_data = 0;
    x = 0.0;
    fx = 0.0;
  else
    x = x_vec(n_data);
    fx = fx_vec(n_data);
  end

  return
end


function [expr morder] = sim_ex_bivarateCoupledOscillator(varargin)
% Simulation:  Bivariate Coupled Oscillator
%
% Description:
%
% Bivariate coupled oscillator example
%
% Author Credits:
%
% Tim Mullen, 2011
%
% References and Code:
%
% N/A
%
% ------------------------------------------------------------------------

% specify the default system of equations
expr_def = {...
    'x1(t) = 0.6*x1(t-1) +  0.65*x2(t-2)+ e1(t)' ...
    'x2(t) = 0.5*x2(t-1) + -0.3*x2(t-2) + e2(t)' ...
};

% set up argument definitions
arg_define(varargin, ...
    arg({'expr','DynamicalEquations'},expr_def,[],'System of equations'), ...
    arg({'morder','ModelOrder'},2,[1 Inf],'Model order. This is mandatory'));

if isempty(morder)
    error('SIFT:sim_examples:badParam','ModelOrder must be specified');
end


function W = threshold_proportional(W, p)
%THRESHOLD_PROPORTIONAL     Proportional thresholding
%
%   W_thr = threshold_proportional(W, p);
%
%   This function "thresholds" the connectivity matrix by preserving a
%   proportion p (0<p<1) of the strongest weights. All other weights, and
%   all weights on the main diagonal (self-self connections) are set to 0.
%
%   Inputs: W,      weighted or binary connectivity matrix
%           p,      proportion of weights to preserve
%                       range:  p=1 (all weights preserved) to
%                               p=0 (no weights preserved)
%
%   Output: W_thr,  thresholded connectivity matrix
%
%
%   Mika Rubinov, U Cambridge,
%   Roan LaPlante, Martinos Center, MGH
%   Zitong Zhang, Penn Engineering

%   Modification history:
%   2010: Original (MR)
%   2012: Bug fix for symmetric matrices (RLP)
%   2015: Improved symmetricity test (ZZ)

n=size(W,1);                                %number of nodes
W(1:n+1:end)=0;                             %clear diagonal

if max(max(abs(W-W.'))) < 1e-10             %if symmetric matrix
    W=triu(W);                              %ensure symmetry is preserved
    ud=2;                                   %halve number of removed links
else
    ud=1;
end

ind=find(W);                                %find all links
E=sortrows([ind W(ind)], -2);               %sort by magnitude
en=round((n^2-n)*p/ud);                     %number of links to be preserved

W(E(en+1:end,1))=0;                         %apply threshold

if ud==2                                    %if symmetric matrix
    W=W+W.';                                %reconstruct symmetry
end


function X = interaction(Xa,Xb)
% Create all interaction terms from two dummy coded design matrices.
% (See Box II in Rouder et al. 2012)
%
% INPUT
% Xa, Xb = [nrObservations nrA] and [nrObservations nrB]
%           design matrices with matching number of observation (rows)
% OUTPUT
% X = Dummy coded design matrix [nrObservations nrA*nrB]
%
nA = size(Xa,2);
nB = size(Xb,2);
Xa = repmat(Xa,[1 nB]);
Xb = repmat(Xb,[1 nA]);
ix = (repmat(1:nA:nA*nB,[nA 1]) + repmat((0:nA-1)',[1 nB]))';
Xa = Xa(:,ix(:));
X= Xa.*Xb;

end


function [ x, error_norm, iter, flag ] = qmr ( A, x, b, M, max_it, tol )

%*****************************************************************************80
%
%% QMR usess the Quasi Minimal Residual Method with no preconditioning.
%
%  Modified:
%
%    27 March 2006
%
%  Reference:
%
%    Richard Barrett, Michael Berry, Tony Chan, James Demmel,
%      June Donato, Jack Dongarra, Victor Eijkhout, Roidan Pozo,
%      Charles Romine, Henk van der Vorst
%    Templates for the Solution of Linear Systems: Building Blocks for
%      Iterative Methods,
%    SIAM Publications, 1993.
%
%  Parameters:
%
%    Input, real A(N,N), the symmetric positive definite matrix.
%
%    Input, real X(N), the initial guess vector.
%
%    Input, real B(N), the right hand side vector.
%
%    Input, real M(N,N), the preconditioning matrix.  M is not used
%    by this routine, and it is included just so that the calling
%    sequence is similar to the other routines.
%
%    Input, integer MAX_IT, the maximum number of iterations.
%
%    Input, real TOL, an error tolerance.
%
%    Output, real X(N), the solution.
%
%    Output, real ERROR_NORM, the norm of the error.
%
%    Output, integer ITER, the number of iterations performed.
%
%    Output, integer FLAG, the return flag.
%    0 = the solution was found to within the specified tolerance.
%    1 = a satisfactory solution was not found.  The iteration limit
%        was exceeded.
%    -1 = breakdown caused by RHO;
%    -2 = breakdown caused by BETA;
%    -3 = breakdown caused by GAMMA;
%    -4 = breakdown caused by DELTA;
%    -5 = breakdown caused by EP;
%    -6 = breakdown caused by XI;
%
  iter = 0;

  bnrm2 = norm ( b );
  if ( bnrm2 == 0.0 )
    bnrm2 = 1.0;
  end

  r = b - A * x;
  error_norm = norm ( r ) / bnrm2;
  errorhist = [ ];
  errorhist(1) = error_norm;

  if ( error_norm < tol )
    flag = 0;
    return
  end

  flag = 1;

  v_tld = r;
  y = v_tld;
  rho = norm ( y );

  w_tld = r;
  z = w_tld;
  xi = norm ( z );

  gamma = 1.0;
  eta = -1.0;
  theta =  0.0;

  for iter = 1 : max_it

    if ( rho == 0.0 )
      flag = -1;
      break
    end

    if ( xi == 0.0 )
      flag = -6;
      break
    end

    v = v_tld / rho;
    y = y / rho;

    w = w_tld / xi;
    z = z / xi;

    delta = z' * y;

    if ( delta == 0.0 )
      flag = -4;
      break
    end

    y_tld = y;
    z_tld = z;
%
%  Compute the direction vector.
%
    if ( 1 < iter )
      p = y_tld - ( xi * delta / ep ) * p;
      q = z_tld - ( rho * delta / ep ) * q;
    else
      p = y_tld;
      q = z_tld;
    end

    p_tld = A * p;
    ep = q' * p_tld;

    if ( ep == 0.0 )
      flag = -5;
      break
    end

    beta = ep / delta;

    if ( beta == 0.0 )
      flag = -2;
      break
    end

    v_tld = p_tld - beta * v;
    y = v_tld;
    rho_1 = rho;
    rho = norm ( y );

    w_tld = ( A' * q ) - ( beta * w );
    z = w_tld;
    xi = norm ( z );
    gamma_1 = gamma;
    theta_1 = theta;

    theta = rho / ( gamma_1 * beta );
    gamma = 1.0 / sqrt ( 1.0 + theta^2 );

    if ( gamma == 0.0 )
      flag = -3;
      break
    end

    eta = -eta * rho_1 * gamma^2 / ( beta * gamma_1^2 );

    if ( 1 < iter )
      d = eta * p + ( ( theta_1 * gamma )^2 ) * d;
      s = eta * p_tld + ( ( theta_1 * gamma )^2 ) * s;
    else
      d = eta * p;
      s = eta * p_tld;
    end
%
%  Update the approximate solution.
%
    x = x + d;
    r = r - s;

    error_norm = norm ( r ) / bnrm2;
    errorhist(iter+1) = error_norm;

    if ( error_norm <= tol )
      flag = 0;
      break
    end

  end

  error_norm = errorhist;

  return
end


function plot_size_distribution_datasets()
colormap = [
228, 229, 97
163, 163, 163
218, 71, 56
219, 135, 45
145, 92, 146
83, 136, 173
106,61,154
225, 119, 174
142, 195, 129
51,160,44
223, 200, 51
92, 172, 158
177,89,40
177,89,40
188, 128, 189
177,89,40
251,154,153
31,120,180]./255;

  box_size_bins = 0:10:600;
  box_size_bin_centers = (box_size_bins(1:(end-1)) + box_size_bins(2:end)) / 2;
  box_size_bins(1) = -inf;
  box_size_bins(end) = inf;
  num_bins = numel(box_size_bins) - 1;

  handles = [];
  legend_labels = {};

  figure; hold on;

  load('data/pascal_voc07_test_annotations.mat');
  gt_w = [pos.x2] - [pos.x1] + 1;
  gt_h = [pos.y2] - [pos.y1] + 1;
  areas = sqrt(gt_w .* gt_h);
  [gt_h,arg_hist] = histc(areas, box_size_bins);
  assert(min(arg_hist) >= 1);
  assert(max(arg_hist) <= numel(box_size_bins) - 1);
  gt_h = gt_h(1:end-1)';
  gt_h = gt_h / sum(gt_h);
  handles(end+1) = plot(box_size_bin_centers, gt_h, '-', ...
    'LineWidth', 1.5, 'Color', colormap(1,:), 'MarkerSize', 10);
  legend_labels{end+1} = 'VOC test 2007';

  val = load('data/ILSVRC2013_val_annotations.mat');
  pos = val.pos;
  gt_w = [pos.x2] - [pos.x1] + 1;
  gt_h = [pos.y2] - [pos.y1] + 1;
  areas = sqrt(gt_w .* gt_h);
  [gt_h,arg_hist] = histc(areas, box_size_bins);
  assert(min(arg_hist) >= 1);
  assert(max(arg_hist) <= numel(box_size_bins) - 1);
  gt_h = gt_h(1:end-1)';
  gt_h = gt_h / sum(gt_h);
  handles(end+1) = plot(box_size_bin_centers, gt_h, '-', ...
    'LineWidth', 1.5, 'Color', colormap(2,:), 'MarkerSize', 10);
  legend_labels{end+1} = 'ILSVRC val 2013';

  val = load('data/coco2014_val_annotations.mat');
  pos = val.pos;
  gt_w = [pos.x2] - [pos.x1] + 1;
  gt_h = [pos.y2] - [pos.y1] + 1;
  areas = sqrt(gt_w .* gt_h);
  [gt_h,arg_hist] = histc(areas, box_size_bins);
  assert(min(arg_hist) >= 1);
  assert(max(arg_hist) <= numel(box_size_bins) - 1);
  gt_h = gt_h(1:end-1)';
  gt_h = gt_h / sum(gt_h);
  handles(end+1) = plot(box_size_bin_centers, gt_h, '-', ...
    'LineWidth', 1.5, 'Color', colormap(3,:), 'MarkerSize', 10);
  legend_labels{end+1} = 'COCO val 2014';

  legend(legend_labels, 'Location', 'northeast');
  legend boxoff;
  xlabel('sqrt(annotation area)');
  ylabel('frequency');
  xlim([0, 400]);
  hei = 10;
  wid = 8;
  set(gcf, 'Units','centimeters', 'Position',[0 0 wid hei]);
  set(gcf, 'PaperPositionMode','auto');
  printpdf('figures/datasets_size_histogram.pdf')

end

function h = get_size_statistics(candidates, im_size, box_size_bins)
  boxes = candidates;
  w = boxes(:,3) - boxes(:,1) + 1;
  h = boxes(:,4) - boxes(:,2) + 1;
  areas = sqrt(w .* h ./ prod(im_size));
  [h,arg_hist] = histc(areas, box_size_bins);

  assert(min(arg_hist) >= 1);
  assert(max(arg_hist) <= numel(box_size_bins) - 1);

  assert(h(end) == 0);
  h = h(1:end-1)';
end


function is_kgraph = is_kuratowski_graph(A,varargin)
% IS_KURATOWSKI_GRAPH Test if a graph can be collapsed to K_3,3 or K_5
%
% is_k = is_kuratowski_graph(A) checks if A can be reduced to K_3,3 or K_5
% by repeated edge contractions.  If so, then is_k=1, otherwise is_k=0.
%
% ... = is_straight_line_drawing(A,...) takes a set of
% key-value pairs or an options structure.  See set_matlab_bgl_options
% for the standard options.
%   No additional options for this function
%
% Example:
%   is_kuratowski_graph(clique_graph(4)) % false, K_4 is not kuratowski
%   is_kuratowski_graph(clique_graph(5)) % true, K_5 is kuratowski
%   is_kuratowski_graph(clique_graph([3,3])) % true, K_3,3 is kuratowski

% David Gleich
% Copyright, Stanford University, 2008

%% History
%  2008-10-05: Initial version
%%

[trans check full2sparse] = get_matlab_bgl_options(varargin{:});
if full2sparse && ~issparse(A), A = sparse(A); end

options = struct();
options = merge_options(options,varargin{:});

if check, check_matlab_bgl(A,struct('sym',1)); end

is_kgraph = planar_test_mex(A,1);


function yi = nearest_interp_1d ( nd, xd, yd, ni, xi )

%*****************************************************************************80
%
%% NEAREST_INTERP_1D evaluates the nearest neighbor interpolant.
%
%  Discussion:
%
%    The nearest neighbor interpolant L(ND,XD,YD)(X) is the piecewise
%    constant function which interpolates the data (XD(I),YD(I)) for I = 1
%    to ND.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    20 August 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer ND, the number of data points.
%    ND must be at least 1.
%
%    Input, real XD(ND,1), the data points.
%
%    Input, real YD(ND,1), the data values.
%
%    Input, integer NI, the number of interpolation points.
%
%    Input, real XI(NI,1), the interpolation points.
%
%    Output, real YI(NI,1), the interpolated values.
%

%
%  KNNSEARCH is a built-in Matlab function which, with this call, returns
%  the indices in XD of the nearest elements to the elements in XI.
%
  idx = knnsearch ( xd, xi );

  yi = yd(idx);

  return
end


% TEST_RING_MIXED_BC_G_NMNN: data function for Neumann boundary condition.

function g = test_ring_mixed_bc_g_nmnn (x, y, ind)

  [theta, r] = cart2pol (x,y);
  switch (ind)
    case 1
      g = -cos (theta) .* exp (x) .* (sin (x.*y) + y.*cos (x.*y)) - ...
          sin (theta) .* exp(x) .* x .* cos (x.*y);
    case 2
      g = cos (theta) .* exp (x) .* (sin (x.*y) + y.*cos (x.*y)) + ...
          sin (theta) .* exp(x) .* x .* cos (x.*y);
    case 3
      g = -x .* exp(x) .* cos (x.*y);
    case 4
      g = -exp(x) .* (sin (x.*y) + y .* cos (x.*y));
    otherwise
      error ('g_nmnn: unknown reference number')
  end

end


function [ grid_weight, grid_point ] = sparse_grid_own ( dim_num, ...
  level_max, rule, point_num )

%*****************************************************************************80
%
%% SPARSE_GRID_OWN computes a sparse grid based on an OWN 1D rule.
%
%  Discussion:
%
%    The 1D quadrature rule is assumed to be Open Weakly Nested.
%    Such rules include Gauss Hermite and Gauss Legendre rules.
%
%    A Smolyak construction is used to create a multidimensional sparse grid.
%
%    The user specifies:
%    * the spatial dimension of the quadrature region,
%    * the level that defines the Smolyak grid,
%    * the rule;
%    * the number of points.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    02 July 2008
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Fabio Nobile, Raul Tempone, Clayton Webster,
%    A Sparse Grid Stochastic Collocation Method for Partial Differential
%    Equations with Random Input Data,
%    SIAM Journal on Numerical Analysis,
%    Volume 46, Number 5, 2008, pages 2309-2345.
%
%  Parameters:
%
%    Input, integer DIM_NUM, the spatial dimension.
%
%    Input, integer LEVEL_MAX, controls the size of the
%    sparse grid.
%
%    Input, integer RULE, the index of the rule.
%    1, "CC", Clenshaw Curtis Closed Fully Nested rule.
%    2, "F1", Fejer 1 Open Fully Nested rule.
%    3, "F2", Fejer 2 Open Fully Nested rule.
%    4, "GP", Gauss Patterson Open Fully Nested rule.
%    5, "GL", Gauss Legendre Open Weakly Nested rule.
%    6, "GH", Gauss Hermite Open Weakly Nested rule.
%    7, "LG", Gauss Laguerre Open Non Nested rule.
%
%    Input, integer POINT_NUM, the number of points in the grid,
%    as determined by LEVELS_INDEX_SIZE_OWN.
%
%    Output, real GRID_WEIGHT(POINT_NUM), the weights.
%
%    Output, real GRID_POINT(DIM_NUM,POINT_NUM), the points.
%
  grid_weight(1:point_num) = 0.0;
  grid_point = zeros ( dim_num, point_num );
%
%  The outer loop generates LEVELs from LEVEL_MIN to LEVEL_MAX.
%
  point_num2 = 0;

  level_min = max ( 0, level_max + 1 - dim_num );

  if ( dim_num == 1 )
    level_min2 = level_min;
  else
    level_min2 = 0;
  end

  for level = level_min2 : level_max
%
%  The middle loop generates the next partition LEVEL_1D(1:DIM_NUM)
%  that adds up to LEVEL.
%
    level_1d = [];
    more = 0;
    h = 0;
    t = 0;

    while ( 1 )

      [ level_1d, more, h, t ] = comp_next ( level, dim_num, level_1d, more, h, t );
%
%  Transform each 1D level to a corresponding 1D order.
%  The relationship is the same as for other OPEN rules.
%  The GL rule differs from the other OPEN rules only in the nesting behavior.
%
      order_1d = level_to_order_open ( dim_num, level_1d );

      grid_base2(1:dim_num) = round ( ( order_1d(1:dim_num) - 1 ) / 2 );
%
%  The product of the 1D orders gives us the number of points in this subgrid.
%
      order_nd = prod ( order_1d(1:dim_num) );
%
%  Compute the weights for this product grid.
%
      grid_weight2 = product_weights ( dim_num, order_1d, order_nd, rule );
%
%  Now determine the coefficient of the weight.
%
      coeff = r8_mop ( level_max - level ) ...
        * r8_choose ( dim_num - 1, level_max - level );
%
%  The inner (hidden) loop generates all points corresponding to given grid.
%  The grid indices will be between -M to +M, where 2*M + 1 = ORDER_1D(DIM).
%
      grid_index2 = multigrid_index_own ( dim_num, order_1d, order_nd );
%
%  Determine the first level of appearance of each of the points.
%  This allows us to flag certain points as being repeats of points
%  generated on a grid of lower level.
%
%  This is SLIGHTLY tricky.
%
      grid_level = index_level_own ( level, level_max, dim_num, order_nd, ...
        grid_index2, grid_base2 );
%
%  Only keep those points which first appear on this level.
%
      for point = 1 : order_nd
%
%  Either a "new" point (increase count, create point, create weight)
%
        if ( grid_level(point) == level )

          point_num2 = point_num2 + 1;

          if ( point_num < point_num2 )
            fprintf ( 1, '\n' );
            fprintf ( 1, 'SPARSE_GRID_OWN - Fatal error!\n' );
            fprintf ( 1, ...
            '  Exceeding maximum point index POINT_NUM = %d\n', point_num );
            error ( 'SPARSE_GRID_OWN - Fatal error!' );
          end

          if ( rule == 5 )
            grid_point(1:dim_num,point_num2) = gl_abscissa ( dim_num, 1, ...
              grid_index2(1:dim_num,point), grid_base2(1:dim_num) );
          elseif ( rule == 6 )
            grid_point(1:dim_num,point_num2) = gh_abscissa ( dim_num, 1, ...
              grid_index2(1:dim_num,point), grid_base2(1:dim_num) );
          else
            fprintf ( 1, '\n' );
            fprintf ( 1, 'SPARSE_GRID_OWN - Fatal error!\n' );
            fprintf ( 1, '  Unrecognized rule number = %d\n', rule );
            error ( 'SPARSE_GRID_OWN - Fatal error!' );
          end

          if ( level_min <= level )
            grid_weight(point_num2) = coeff * grid_weight2(point);
          end
%
%  or an already existing point (create point temporarily, find match,
%  add weight to matched point's weight).
%
        else

          if ( level_min <= level )

            if ( rule == 5 )
              grid_point_temp(1:dim_num) = gl_abscissa ( dim_num, 1, ...
                grid_index2(1:dim_num,point), grid_base2(1:dim_num) );
            elseif ( rule == 6 )
              grid_point_temp(1:dim_num) = gh_abscissa ( dim_num, 1, ...
                grid_index2(1:dim_num,point), grid_base2(1:dim_num) );
            else
              fprintf ( 1, '\n' );
              fprintf ( 1, 'SPARSE_GRID_OWN - Fatal error!\n' );
              fprintf ( 1, '  Unrecognized rule number = %d\n', rule );
              error ( 'SPARSE_GRID_OWN - Fatal error!' );
            end

            point3 = -1;

            for point2 = 1 : point_num2
              if ( all ( grid_point(1:dim_num,point2) == grid_point_temp(1:dim_num)' ) )
                point3 = point2;
                break
              end
            end

            if ( point3 == -1 )
              fprintf ( 1, '\n' );
              fprintf ( 1, 'SPARSE_GRID_OWN - Fatal error!\n' );
              fprintf ( 1, '  Could not match point.\n' );
              error ( 'SPARSE_GRID_OWN - Fatal error!' );
            end

            grid_weight(point3) = grid_weight(point3) ...
              + coeff * grid_weight2(point);

          end

        end

      end

      if ( ~more )
        break
      end

    end

  end

  if ( point_num2 < point_num )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'SPARSE_GRID_OWN - Fatal error!\n' );
    fprintf ( 1,'  Set fewer points than POINT_NUM = %d\n', point_num );
    error ( 'SPARSE_GRID_OWN - Fatal error!' );
  end

  return
end


function [out] = recharge_6(p1,p2,S,dt)
%recharge_6

% Copyright (C) 2019, 2021 Wouter J.M. Knoben, Luca Trotter
% This file is part of the Modular Assessment of Rainfall-Runoff Models
% Toolbox (MARRMoT).
% MARRMoT is a free software (GNU GPL v3) and distributed WITHOUT ANY
% WARRANTY. See <https://www.gnu.org/licenses/> for details.

% Flux function
% ------------------
% Description:  Recharge to fulfil evaporation demand if the receiving
%               store is below a threshold
% Constraints:  f <= S/dt
%               S >= 0      prevents complex numbers
% @(Inputs):    p1   - time coefficient [d-1]
%               p2   - non-linear scaling [mm]
%               S    - current storage [mm]
%               dt   - time step size [d]

out = min(max(S/dt,0),p1.*max(S,0).^p2);

end


%% Analyzing Neural Time Series Data
% Matlab code for Chapter 34
% Mike X Cohen
%
% This code accompanies the book, titled "Analyzing Neural Time Series Data"
% (MIT Press). Using the code without following the book may lead to confusion,
% incorrect data analyses, and misinterpretations of results.
% Mike X Cohen assumes no responsibility for inappropriate or incorrect use of this code.

%% load sample data

load sampleEEGdata

% note: Most of these figures take a while to generate. Have patience!

%% extract TF power (create data that are used for the rest of this chapter)

% definitions, selections...
chan2use = 'fcz';

min_freq = 3;
max_freq = 30;
num_frex = 20;

% define wavelet parameters
time = -1:1/EEG.srate:1;
frex = logspace(log10(min_freq),log10(max_freq),num_frex);
s    = logspace(log10(3),log10(10),num_frex)./(2*pi*frex);

% definte convolution parameters
n_wavelet            = length(time);
n_data               = EEG.pnts*EEG.trials;
n_convolution        = n_wavelet+n_data-1;
n_conv_pow2          = pow2(nextpow2(n_convolution));
half_of_wavelet_size = (n_wavelet-1)/2;

% note that you don't need the wavelet itself, you need the FFT of the wavelet
wavelets = zeros(num_frex,n_conv_pow2);
for fi = 1:num_frex
    wavelets(fi,:)  = fft( sqrt(1/(s(fi)*sqrt(pi))) * exp(2*1i*pi*frex(fi).*time) .* exp(-time.^2./(2*(s(fi)^2))) , n_conv_pow2 );
end

% get FFT of data
eegfft = fft(reshape(EEG.data(strcmpi(chan2use,{EEG.chanlocs.labels}),:,:),1,EEG.pnts*EEG.trials),n_conv_pow2);

% initialize
eegpower = zeros(num_frex,EEG.pnts,EEG.trials); % frequencies X time X trials
eegphase = zeros(num_frex,EEG.pnts,EEG.trials); % frequencies X time X trials

% loop through frequencies and compute synchronization
for fi=1:num_frex

    % convolution
    eegconv = ifft(wavelets(fi,:).*eegfft);
    eegconv = eegconv(1:n_convolution);
    eegconv = eegconv(half_of_wavelet_size+1:end-half_of_wavelet_size);

    % reshape to time X trials
    eegpower(fi,:,:) = abs(reshape(eegconv,EEG.pnts,EEG.trials)).^2;
    eegphase(fi,:,:) = exp(1i*angle(reshape(eegconv,EEG.pnts,EEG.trials)));
end

% remove edge artifacts
time_s = dsearchn(EEG.times',-500);
time_e = dsearchn(EEG.times',1200);

eegpower = eegpower(:,time_s:time_e,:);
tftimes  = EEG.times(time_s:time_e);
nTimepoints = numel(tftimes);

%% Figure 34.1

voxel_pval   = 0.01;
cluster_pval = 0.05;

% note: try to use 1000 or more permutations for real data
n_permutes = 1000;

baseidx(1) = dsearchn(tftimes',-500);
baseidx(2) = dsearchn(tftimes',-100);

% compute actual t-test of difference
realbaselines = squeeze(mean(eegpower(:,baseidx(1):baseidx(2),:),2));
realmean      = 10*log10(bsxfun(@rdivide, mean(eegpower,3), mean(realbaselines,2)));

% initialize null hypothesis matrices
permuted_maxvals = zeros(n_permutes,2,num_frex);
permuted_vals    = zeros(n_permutes,num_frex,numel(tftimes));
max_clust_info   = zeros(n_permutes,1);

for permi=1:n_permutes
    cutpoint = randsample(2:nTimepoints-diff(baseidx)-2,1);
    permuted_vals(permi,:,:) = 10*log10(bsxfun(@rdivide,mean(eegpower(:,[cutpoint:end 1:cutpoint-1],:),3),mean(realbaselines,2)) );
    % btw, using bsxfun instead of repmat increases the speed
    % of this loop by a factor of ~5.
end

zmap = (realmean-squeeze(mean(permuted_vals))) ./ squeeze(std(permuted_vals));
threshmean = realmean;
threshmean(abs(zmap)<norminv(1-voxel_pval))=0;

figure
subplot(221)
contourf(tftimes,frex,realmean,40,'linecolor','none')
axis square
set(gca,'clim',[-3 3],'xlim',[-500 1200])
title('power map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

subplot(222)
contourf(tftimes,frex,zmap,40,'linecolor','none')
axis square
set(gca,'clim',[-3 3],'xlim',[-500 1200])
title('unthresholded Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

subplot(223)
contourf(tftimes,frex,threshmean,40,'linecolor','none')
axis square
set(gca,'clim',[-3 3],'xlim',[-500 1200])
title('Uncorrected power map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

% this time, the cluster correction will be done on the permuted data, thus
% making no assumptions about parameters for p-values
for permi = 1:n_permutes

    % for cluster correction, apply uncorrected threshold and get maximum cluster sizes
    fakecorrsz = squeeze((permuted_vals(permi,:,:)-mean(permuted_vals,1)) ./ std(permuted_vals,[],1) );
    fakecorrsz(abs(fakecorrsz)<norminv(1-voxel_pval))=0;

    % get number of elements in largest supra-threshold cluster
    clustinfo = bwconncomp(fakecorrsz);
    max_clust_info(permi) = max([ 0 cellfun(@numel,clustinfo.PixelIdxList) ]); % the zero accounts for empty maps
    % using cellfun here eliminates the need for a slower loop over cells
end

% apply cluster-level corrected threshold
zmapthresh = zmap;
% uncorrected pixel-level threshold
zmapthresh(abs(zmapthresh)<norminv(1-voxel_pval))=0;
% find islands and remove those smaller than cluster size threshold
clustinfo = bwconncomp(zmapthresh);
clust_info = cellfun(@numel,clustinfo.PixelIdxList);
clust_threshold = prctile(max_clust_info,100-cluster_pval*100);

% identify clusters to remove
whichclusters2remove = find(clust_info<clust_threshold);

% remove clusters
for i=1:length(whichclusters2remove)
    zmapthresh(clustinfo.PixelIdxList{whichclusters2remove(i)})=0;
end

subplot(224)
contourf(tftimes,frex,zmapthresh,40,'linecolor','none')
axis square
set(gca,'clim',[-3 3],'xlim',[-500 1200])
title('Cluster-corrected Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

%% Figure 34.3

voxel_pval = 0.05;
mcc_voxel_pval = 0.05; % mcc = multiple comparisons correction
mcc_cluster_pval = 0.05;

% note: try to use 1000 or more permutations for real data
n_permutes = 1000;

real_condition_mapping = [ -ones(1,floor(EEG.trials/2)) ones(1,ceil(EEG.trials/2)) ];

% compute actual t-test of difference (using unequal N and std)
tnum   = squeeze(mean(eegpower(:,:,real_condition_mapping==-1),3) - mean(eegpower(:,:,real_condition_mapping==1),3));
tdenom = sqrt( (std(eegpower(:,:,real_condition_mapping==-1),0,3).^2)./sum(real_condition_mapping==-1) + (std(eegpower(:,:,real_condition_mapping==1),0,3).^2)./sum(real_condition_mapping==1) );
real_t = tnum./tdenom;

% initialize null hypothesis matrices
permuted_tvals  = zeros(n_permutes,num_frex,nTimepoints);
max_pixel_pvals = zeros(n_permutes,2);
max_clust_info  = zeros(n_permutes,1);

% generate pixel-specific null hypothesis parameter distributions
for permi = 1:n_permutes
    fake_condition_mapping = sign(randn(EEG.trials,1));

    % compute t-map of null hypothesis
    tnum   = squeeze(mean(eegpower(:,:,fake_condition_mapping==-1),3)-mean(eegpower(:,:,fake_condition_mapping==1),3));
    tdenom = sqrt( (std(eegpower(:,:,fake_condition_mapping==-1),0,3).^2)./sum(fake_condition_mapping==-1) + (std(eegpower(:,:,fake_condition_mapping==1),0,3).^2)./sum(fake_condition_mapping==1) );
    tmap   = tnum./tdenom;

    % save all permuted values
    permuted_tvals(permi,:,:) = tmap;

    % save maximum pixel values
    max_pixel_pvals(permi,:) = [ min(tmap(:)) max(tmap(:)) ];

    % for cluster correction, apply uncorrected threshold and get maximum cluster sizes
    % note that here, clusters were obtained by parametrically thresholding
    % the t-maps
    tmap(abs(tmap)<tinv(1-voxel_pval,EEG.trials-1))=0;

    % get number of elements in largest supra-threshold cluster
    clustinfo = bwconncomp(tmap);
    max_clust_info(permi) = max([ 0 cellfun(@numel,clustinfo.PixelIdxList) ]); % notes: cellfun is superfast, and the zero accounts for empty maps
end

% now compute Z-map
zmap = (real_t-squeeze(mean(permuted_tvals,1)))./squeeze(std(permuted_tvals));

figure
subplot(221)
contourf(tftimes,frex,zmap,40,'linecolor','none')
axis square
set(gca,'clim',[-3 3],'xlim',[-500 1200])
title('Unthresholded Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

% apply uncorrected threshold
subplot(222)
contourf(tftimes,frex,zmap,40,'linecolor','none')
zmapthresh = zmap;
zmapthresh(abs(zmapthresh)<norminv(1-voxel_pval))=false;
zmapthresh=logical(zmapthresh);
hold on
contour(tftimes,frex,zmapthresh,1,'linecolor','k')

axis square
set(gca,'clim',[-3 3],'xlim',[-500 1200])
title('Unthresholded Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

% apply pixel-level corrected threshold
lower_threshold = prctile(max_pixel_pvals(:,1),    mcc_voxel_pval*100/2);
upper_threshold = prctile(max_pixel_pvals(:,2),100-mcc_voxel_pval*100/2);

zmapthresh = zmap;
zmapthresh(zmapthresh>lower_threshold & zmapthresh<upper_threshold)=0;
subplot(223)
contourf(tftimes,frex,zmapthresh,40,'linecolor','none')
axis square
set(gca,'clim',[-3 3],'xlim',[-500 1200])
title('Pixel-corrected Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

% apply cluster-level corrected threshold
zmapthresh = zmap;
% uncorrected pixel-level threshold
zmapthresh(abs(zmapthresh)<norminv(1-voxel_pval))=0;
% find islands and remove those smaller than cluster size threshold
clustinfo = bwconncomp(zmapthresh);
clust_info = cellfun(@numel,clustinfo.PixelIdxList);
clust_threshold = prctile(max_clust_info,100-mcc_cluster_pval*100);

% identify clusters to remove
whichclusters2remove = find(clust_info<clust_threshold);

% remove clusters
for i=1:length(whichclusters2remove)
    zmapthresh(clustinfo.PixelIdxList{whichclusters2remove(i)})=0;
end

subplot(224)
contourf(tftimes,frex,zmapthresh,40,'linecolor','none')
axis square
set(gca,'clim',[-3 3],'xlim',[-500 1200])
title('Cluster-corrected Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

%% Figure 34.4

voxel_pval = 0.01;
mcc_voxel_pval = 0.05; % mcc = multiple comparisons correction
mcc_cluster_pval = 0.05;

% note: try to use 1000 or more permutations for real data
n_permutes = 1000;

rts=zeros(1,EEG.trials);
for ei=1:EEG.trials
    % In this task, the button press always followed the stimulus at
    % time=0. Thus, finding the RT involves finding the latency of the
    % event that occurs after the time=0 event.
    % If you follow a procedure like this in your data, you may need to
    % include special exceptions, e.g., if there was no response or if
    % a non-response marker could have occurred between stimulus and response.
    time0event = find(cell2mat(EEG.epoch(ei).eventlatency)==0);
    rts(ei)    = EEG.epoch(ei).eventlatency{time0event+1};
end

% rank-transform RTs
rtsrank = tiedrank(rts);

% rank-transform power data (must be transformed)
eegpowerreshaped = reshape(eegpower,num_frex*nTimepoints,EEG.trials)';
eegpowerrank = tiedrank(eegpowerreshaped);

% technically, you want to perform a correlation, but a linear least-squares fit provides the same conceptual results
% while being ~15 times faster, and we don't care here about the actual scale of the data. For completeness,
% the following line shows you how to compute the Spearman correlation coefficient
% realcorrs = 1-6*sum((eegpowerrank-repmat(rtsrank',1,size(eegpowerrank,2))).^2)/(EEG.trials*(EEG.trials^2-1));
realcorrs = (rtsrank*rtsrank')\rtsrank*eegpowerrank;
realcorrs = reshape(realcorrs,num_frex,nTimepoints);

% initialize null hypothesis matrices
permuted_rvals  = zeros(n_permutes,num_frex,nTimepoints);
max_pixel_rvals = zeros(n_permutes,2);
max_clust_info  = zeros(n_permutes,1);

% generate pixel-specific null hypothesis parameter distributions
for permi = 1:n_permutes
    fake_rt_mapping = rtsrank(randperm(EEG.trials));

    % compute t-map of null hypothesis
    fakecorrs = (fake_rt_mapping*fake_rt_mapping')\fake_rt_mapping*eegpowerrank;

    % reshape to 2D map for cluster-correction
    fakecorrs = reshape(fakecorrs,num_frex,nTimepoints);

    % save all permuted values
    permuted_rvals(permi,:,:) = fakecorrs;

    % save maximum pixel values
    max_pixel_rvals(permi,:) = [ min(fakecorrs(:)) max(fakecorrs(:)) ];
end

% this time, the cluster correction will be done on the permuted data, thus
% making no assumptions about parameters for p-values
for permi = 1:n_permutes

    % indices of permutations to include in thresholding at this iteration
    perms2use4distribution = true(1,n_permutes);
    perms2use4distribution(permi) = 0;

    % for cluster correction, apply uncorrected threshold and get maximum cluster sizes
    fakecorrsz = squeeze((permuted_rvals(permi,:,:)-mean(permuted_rvals(perms2use4distribution,:,:),1)) ./ std(permuted_rvals(perms2use4distribution,:,:),[],1) );
    fakecorrsz(abs(fakecorrsz)<norminv(1-voxel_pval))=0;

    % get number of elements in largest supra-threshold cluster
    clustinfo = bwconncomp(fakecorrsz);
    max_clust_info(permi) = max([ 0 cellfun(@numel,clustinfo.PixelIdxList) ]); % the zero accounts for empty maps
end

% now compute Z-map
zmap = (realcorrs-squeeze(mean(permuted_rvals,1)))./squeeze(std(permuted_rvals));

figure
subplot(221)
contourf(tftimes,frex,zmap,40,'linecolor','none')
axis square
set(gca,'clim',[-4 4],'xlim',[-500 1200])
title('Unthresholded Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

% apply uncorrected threshold
zmapthresh = zmap;
zmapthresh(abs(zmapthresh)<norminv(1-voxel_pval))=0;
zmapthresh=logical(zmapthresh);
subplot(222)
contourf(tftimes,frex,zmap,40,'linecolor','none')
hold on
contour(tftimes,frex,zmapthresh,1,'linecolor','k')
axis square
set(gca,'clim',[-4 4],'xlim',[-500 1200])
title('Uncorrected thresholded Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

% apply pixel-level corrected threshold
lower_threshold = prctile(max_pixel_rvals(:,1),    mcc_voxel_pval*100/2);
upper_threshold = prctile(max_pixel_rvals(:,2),100-mcc_voxel_pval*100/2);

zmapthresh = zmap;
zmapthresh(realcorrs>lower_threshold & realcorrs<upper_threshold)=0;
subplot(223)
contourf(tftimes,frex,zmapthresh,40,'linecolor','none')
axis square
set(gca,'clim',[-4 4],'xlim',[-500 1200])
title('Pixel-corrected Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

% apply cluster-level corrected threshold
zmapthresh = zmap;
% uncorrected pixel-level threshold
zmapthresh(abs(zmapthresh)<norminv(1-voxel_pval))=0;
% find islands and remove those smaller than cluster size threshold
clustinfo = bwconncomp(zmapthresh);
clust_info = cellfun(@numel,clustinfo.PixelIdxList);
clust_threshold = prctile(max_clust_info,100-mcc_cluster_pval*100);

% identify clusters to remove
whichclusters2remove = find(clust_info<clust_threshold);

% remove clusters
for i=1:length(whichclusters2remove)
    zmapthresh(clustinfo.PixelIdxList{whichclusters2remove(i)})=0;
end

subplot(224)
contourf(tftimes,frex,zmapthresh,40,'linecolor','none')
axis square
set(gca,'clim',[-4 4],'xlim',[-500 1200])
title('Cluster-corrected Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

%% Figure 34.5

chan2use = 'o1';
time2use = dsearchn(EEG.times',[0 250]');
freq2use = dsearchn(frex',10);

eegfft = fft(reshape(EEG.data(strcmpi(chan2use,{EEG.chanlocs.labels}),:,:),1,EEG.pnts*EEG.trials),n_conv_pow2);
eegconv = ifft(wavelets(freq2use,:).*eegfft);
eegconv = eegconv(1:n_convolution);
eegconv = eegconv(half_of_wavelet_size+1:end-half_of_wavelet_size);

% reshape to time X trials
temp  = abs(reshape(eegconv,EEG.pnts,EEG.trials)).^2;
o1power = zscore(mean(temp(time2use(1):time2use(2),:),1));

% define covariates (RT and trial number)
X = [ zscore(rts') o1power' ]';
eegpowerrank = tiedrank(eegpowerreshaped)';

figure
subplot(311)
imagesc(X)

subplot(212)
imagesc(eegpowerrank)

%% Figure 34.6

voxel_pval = 0.01;
mcc_cluster_pval = 0.05;

% note: try to use 1000 or more permutations for real data
n_permutes = 1000;

realbeta = (X*X')\X*eegpowerrank';
realbeta = reshape(realbeta,[2 num_frex nTimepoints]);

% initialize null hypothesis matrices
permuted_bvals = zeros(n_permutes,2,num_frex,nTimepoints);
max_clust_info = zeros(n_permutes,2);

% generate pixel-specific null hypothesis parameter distributions
for permi = 1:n_permutes

    % randomly shuffle trial order
    fakeX = X(:,randperm(EEG.trials));

    % compute beta-map of null hypothesis
    fakebeta = (fakeX*fakeX')\fakeX*eegpowerrank';

    % reshape to 2D map for cluster-correction
    fakebeta = reshape(fakebeta,[2 num_frex nTimepoints ]);

    % save all permuted values
    permuted_bvals(permi,:,:,:) = fakebeta;
end

% this time, the cluster correction will be done on the permuted data, thus
% making no assumptions about parameters for p-values
for permi = 1:n_permutes

    for testi=1:2
        % for cluster correction, apply uncorrected threshold and get maximum cluster sizes
        fakecorrsz = squeeze((permuted_bvals(permi,testi,:,:)-mean(permuted_bvals(:,testi,:,:),1)) ./ std(permuted_bvals(:,testi,:,:),[],1) );
        fakecorrsz(abs(fakecorrsz)<norminv(1-voxel_pval))=0;
        % get number of elements in largest supra-threshold cluster
        clustinfo = bwconncomp(fakecorrsz);
        max_clust_info(permi,testi) = max([ 0 cellfun(@numel,clustinfo.PixelIdxList) ]); % the zero accounts for empty maps
    end
end

figure
for testi=1:2

    % now compute Z-map
    zmap = (squeeze(realbeta(testi,:,:))-squeeze(mean(permuted_bvals(:,testi,:,:),1))) ./ squeeze(std(permuted_bvals(:,testi,:,:),[],1));

    subplot(2,3,1+(testi-1)*3)
    contourf(tftimes,frex,zmap,40,'linecolor','none')
    axis square
    set(gca,'clim',[-3 3],'xlim',[-500 1200])
    title('Unthresholded Z map')
    xlabel('Time (ms)'), ylabel('Frequency (Hz)')

    % apply uncorrected threshold
    zmapthresh = zmap;
    zmapthresh(abs(zmapthresh)<norminv(1-voxel_pval))=0;
    subplot(2,3,2+(testi-1)*3)
    contourf(tftimes,frex,zmapthresh,40,'linecolor','none')
    axis square
    set(gca,'clim',[-3 3],'xlim',[-500 1200])
    title('Uncorrected thresholded Z map')
    xlabel('Time (ms)'), ylabel('Frequency (Hz)')

    % apply cluster-level corrected threshold
    zmapthresh = zmap;
    % uncorrected pixel-level threshold
    zmapthresh(abs(zmapthresh)<norminv(1-voxel_pval))=0;
    % find islands and remove those smaller than cluster size threshold
    clustinfo = bwconncomp(zmapthresh);
    clust_info = cellfun(@numel,clustinfo.PixelIdxList);
    clust_threshold = prctile(max_clust_info(:,testi),100-mcc_cluster_pval*100);

    % identify clusters to remove
    whichclusters2remove = find(clust_info<clust_threshold);

    % remove clusters
    for i=1:length(whichclusters2remove)
        zmapthresh(clustinfo.PixelIdxList{whichclusters2remove(i)})=0;
    end

    subplot(2,3,3+(testi-1)*3)
    contourf(tftimes,frex,zmapthresh,40,'linecolor','none')
    axis square
    set(gca,'clim',[-3 3],'xlim',[-500 1200])
    title('Cluster-corrected Z map')
    xlabel('Time (ms)'), ylabel('Frequency (Hz)')
end

%% Figure 34.7

a = rand(10000,1);
b = rand(10000,1);

figure
clear h

subplot(221)
[y,x] = hist(a,50);
h(1)=bar(x,y,'histc');
set(gca,'xlim',[-.05 1.05])

subplot(222)
[y,x] = hist(b,50);
h(2)=bar(x,y,'histc');
set(gca,'xlim',[-.05 1.05])

subplot(212)
[y,x] = hist(atanh(a-b),50);
h(3)=bar(x,y,'histc');
set(gca,'xlim',[-2 2])
title('ITPC differences')
xlabel('difference value'), ylabel('Count')

set(h,'linestyle','none','facecolor','k')

%% Figure 34.8

% The code to produce this figure is presented in chapter19.m, between the
% cells for figures 19.6 and 19.7. To generate figure 34.8, you will need to run
% the code for figures 19.2-6.

%% Figure 34.9

voxel_pval   = 0.01;
cluster_pval = 0.05;

% note: try to use 1000 or more permutations for real data
n_permutes = 1000;

% compute actual t-test of difference
realitpc = squeeze(abs(mean(eegphase,3)));

% initialize null hypothesis matrices
permuted_maxvals = zeros(n_permutes,2,num_frex);
permuted_vals    = zeros(n_permutes,num_frex,EEG.pnts);
max_clust_info   = zeros(n_permutes,1);
eegtemp          = zeros(size(realitpc));

for permi=1:n_permutes
    for triali=1:EEG.trials
        cutpoint = randsample(2:nTimepoints-2,1);
        eegtemp(:,:,triali) = eegphase(:,[cutpoint:end 1:cutpoint-1],triali);
    end
    permuted_vals(permi,:,:) = squeeze(abs(mean(eegtemp,3)));

    % note: the following lines produce fairly similar results as the loop above
    % cutpoint = randsample(2:nTimepoints-2,1);
    %permuted_vals(permi,:,:) = squeeze(abs(mean(eegphase(:,[cutpoint:end 1:cutpoint-1],:),3)));
end

zmap = (realitpc-squeeze(mean(permuted_vals))) ./ squeeze(std(permuted_vals));
threshmean = realitpc;
threshmean(abs(zmap)<norminv(1-voxel_pval))=0;

figure
subplot(221)
contourf(EEG.times,frex,realitpc,40,'linecolor','none')
axis square
set(gca,'clim',[0 .5],'xlim',[-200 1000])
title('power map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

subplot(222)
contourf(EEG.times,frex,zmap,40,'linecolor','none')
axis square
set(gca,'clim',[-5 5],'xlim',[-200 1000])
title('unthresholded Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

subplot(223)
contourf(EEG.times,frex,threshmean,40,'linecolor','none')
axis square
set(gca,'clim',[0 .5],'xlim',[-200 1000])
title('Uncorrected power map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

% this time, the cluster correction will be done on the permuted data, thus
% making no assumptions about parameters for p-values
for permi = 1:n_permutes

    % for cluster correction, apply uncorrected threshold and get maximum cluster sizes
    fakecorrsz = squeeze((permuted_vals(permi,:,:)-mean(permuted_vals,1)) ./ std(permuted_vals,[],1) );
    fakecorrsz(abs(fakecorrsz)<norminv(1-voxel_pval))=0;

    % get number of elements in largest supra-threshold cluster
    clustinfo = bwconncomp(fakecorrsz);
    max_clust_info(permi) = max([ 0 cellfun(@numel,clustinfo.PixelIdxList) ]); % the zero accounts for empty maps
    % using cellfun here eliminates the need for a slower loop over cells
end

% apply cluster-level corrected threshold
zmapthresh = realitpc;
% uncorrected pixel-level threshold
zmapthresh(abs(zmap)<norminv(1-voxel_pval))=0;
% find islands and remove those smaller than cluster size threshold
clustinfo = bwconncomp(zmapthresh);
clust_info = cellfun(@numel,clustinfo.PixelIdxList);
clust_threshold = prctile(max_clust_info,100-cluster_pval*100);

% identify clusters to remove
whichclusters2remove = find(clust_info<clust_threshold);

% remove clusters
for i=1:length(whichclusters2remove)
    zmapthresh(clustinfo.PixelIdxList{whichclusters2remove(i)})=0;
end

subplot(224)
contourf(EEG.times,frex,zmapthresh,40,'linecolor','none')
axis square
set(gca,'clim',[0 .5],'xlim',[-200 1000])
title('Cluster-corrected Z map')
xlabel('Time (ms)'), ylabel('Frequency (Hz)')

%% Figure 33.5

% The code for figures 33.5/6 are presented here and in the next cell. You
% will need first to run the code for figure 34.3 to run this code.

% compute actual t-test of difference (using unequal N and std)
tnum   = squeeze(mean(eegpower(4,400,real_condition_mapping==-1),3) - mean(eegpower(4,400,real_condition_mapping==1),3));
tdenom = sqrt( (std(eegpower(4,400,real_condition_mapping==-1),0,3).^2)./sum(real_condition_mapping==-1) + (std(eegpower(4,400,real_condition_mapping==1),0,3).^2)./sum(real_condition_mapping==1) );
real_t = tnum./tdenom;

n_permutes = round(linspace(100,3000,200));
zvals   = zeros(size(n_permutes));

for grandpermi=1:length(n_permutes)

    % initialize null hypothesis matrices
    permuted_tvals  = zeros(n_permutes(grandpermi),1);

    % generate pixel-specific null hypothesis parameter distributions
    for permi = 1:n_permutes(grandpermi)
        fake_condition_mapping = sign(randn(EEG.trials,1));
        % compute t-map of null hypothesis
        tnum   = squeeze(mean(eegpower(4,400,fake_condition_mapping==-1),3)-mean(eegpower(4,400,fake_condition_mapping==1),3));
        tdenom = sqrt( (std(eegpower(4,400,fake_condition_mapping==-1),0,3).^2)./sum(fake_condition_mapping==-1) + (std(eegpower(4,400,fake_condition_mapping==1),0,3).^2)./sum(fake_condition_mapping==1) );
        % save all permuted values
        permuted_tvals(permi) = tnum./tdenom;
    end

    zvals(grandpermi) = (real_t-mean(permuted_tvals))/std(permuted_tvals);

    % display progress
    if mod(grandpermi,20)==0, disp([ 'Metapermutation #' num2str(grandpermi) ]); end
end

figure
subplot(211)
plot(n_permutes,zvals)
xlabel('Number of iterations'), ylabel('Z-value')

subplot(212)
hist(zvals,30)
xlabel('Z-value at different runs of permutation test'), ylabel('Count')

%% Figure 33.6

% compute actual t-test of difference (using unequal N and std)
tnum   = squeeze(mean(eegpower(:,:,real_condition_mapping==-1),3) - mean(eegpower(:,:,real_condition_mapping==1),3));
tdenom = sqrt( (std(eegpower(:,:,real_condition_mapping==-1),0,3).^2)./sum(real_condition_mapping==-1) + (std(eegpower(:,:,real_condition_mapping==1),0,3).^2)./sum(real_condition_mapping==1) );
real_t = tnum./tdenom;

n_permutes = round(linspace(100,3000,200));
zvals = zeros(length(n_permutes),size(tnum,1),size(tnum,2));

for grandpermi=1:length(n_permutes)

    % initialize null hypothesis matrices
    permuted_tvals  = zeros(n_permutes(grandpermi),size(tnum,1),size(tnum,2));

    % generate pixel-specific null hypothesis parameter distributions
    for permi = 1:n_permutes(grandpermi)
        fake_condition_mapping = sign(randn(EEG.trials,1));
        % compute t-map of null hypothesis
        tnum   = squeeze(mean(eegpower(:,:,fake_condition_mapping==-1),3)-mean(eegpower(:,:,fake_condition_mapping==1),3));
        tdenom = sqrt( (std(eegpower(:,:,fake_condition_mapping==-1),0,3).^2)./sum(fake_condition_mapping==-1) + (std(eegpower(:,:,fake_condition_mapping==1),0,3).^2)./sum(fake_condition_mapping==1) );
        % save all permuted values
        permuted_tvals(permi,:,:) = tnum./tdenom;
    end

    zvals(grandpermi,:,:) = (real_t-squeeze(mean(permuted_tvals)))./squeeze(std(permuted_tvals));

    % display progress
    if mod(grandpermi,20)==0, disp([ 'Metapermutation # ' num2str(grandpermi) ]); end
end

figure
subplot(211)
plot(n_permutes,squeeze(zvals(:,4,400)))
subplot(212)
hist(squeeze(zvals(:,4,400)),30)

zvalsall = reshape(zvals,length(n_permutes),size(tnum,1)*size(tnum,2));
figure
plot(mean(zvalsall,1),std(zvalsall,[],1),'.')
set(gca,'xlim',[-3.5 3.5],'ylim',[0 .12])
xlabel('Average Z-statistic')
ylabel('Standard deviation of Z-statistics')

figure
clear h

[~,z0]=min(abs(mean(zvalsall,1)));
[x0,y0]=ind2sub(size(tnum),z0);
[yy,xx]=hist(squeeze(zvals(:,x0,y0)),30);
h(1) = bar(xx,yy,'histc');
hold on
plot([0 0],get(gca,'ylim'),'k:')

[~,z2]=min(abs(mean(zvalsall,1)-2));
[x2,y2]=ind2sub(size(tnum),z2);
[yy,xx]=hist(squeeze(zvals(:,x2,y2)),30);
h(2) = bar(xx,yy,'histc');
plot([2 2],get(gca,'ylim'),'k:')

[~,z3]=min(abs(mean(zvalsall,1)--3));
[x3,y3]=ind2sub(size(tnum),z3);
[yy,xx]=hist(squeeze(zvals(:,x3,y3)),30);
hold on
h(3) = bar(xx,yy,'histc');
plot([-3 -3],get(gca,'ylim'),'k:')

set(h,'linestyle','none')
set(gca,'xlim',[-3.5 3.5])
xlabel('Z-value')
ylabel('Count of possible z-values')

%% end.


function [F] = CutOffFilter2D(Nc,frac)

% function [F] = CutOffFilter2D(Nc,frac)
% Purpose : Initialize 2D cut off filter matrix of order Norderin

Globals2D;

filterdiag = ones(Np,1);

% build exponential filter
sk = 1;
for i=0:N
  for j=0:N-i
    if (i+j>=Nc)
      filterdiag(sk) = frac;
    end
    sk = sk+1;
  end
end

F = V*diag(filterdiag)*invV;
return;


classdef prtPreProcPca < prtPreProc
    % prtPreProcPca   Principle Component Analysis
    %
    %   PCA = prtPreProcPca creates a Principle Component Analysis selfect.
    %
    %   PCA = prtPreProcPca('nComponents',N) constructs a
    %   prtPreProcPCP selfect PCA with nComponents set to the value N.
    %
    %   A prtPreProcPca selfect has the following properites:
    %
    %   nComponents    - The number of principle componenets
    %
    %   A prtPreProcPca selfect also inherits all properties and functions from
    %   the prtAction class
    %
    %   Example:
    %
    %   dataSet = prtDataGenFeatureSelection;    % Load a data set
    %   pca = prtPreProcPca;            % Create a prtPreProcPca selfect
    %
    %   pca = pca.train(dataSet);       % Train the prtPreProcPca selfect
    %   dataSetNew = pca.run(dataSet);  % Run
    %
    %   % Plot
    %   plot(dataSetNew);
    %   title('PCA Projected Data');
    %
    %   See Also: prtPreProc, prtPreProcPca, prtPreProcPls,
    %   prtPreProcHistEq, prtPreProcZeroMeanColumns, prtPreProcLda,
    %   prtPreProcZeroMeanRows, prtPreProcLogDisc, prtPreProcZmuv,
    %   prtPreProcMinMaxRows

    % Copyright (c) 2013 New Folder Consulting
    %
    % Permission is hereby granted, free of charge, to any person obtaining a
    % copy of this software and associated documentation files (the
    % "Software"), to deal in the Software without restriction, including
    % without limitation the rights to use, copy, modify, merge, publish,
    % distribute, sublicense, and/or sell copies of the Software, and to permit
    % persons to whom the Software is furnished to do so, subject to the
    % following conditions:
    %
    % The above copyright notice and this permission notice shall be included
    % in all copies or substantial portions of the Software.
    %
    % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    % OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    % MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
    % NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    % DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
    % OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
    % USE OR OTHER DEALINGS IN THE SOFTWARE.

    properties (SetAccess=private)
        name = 'Principal Component Analysis' % Principal Component Analysis
        nameAbbreviation = 'PCA'  % PCA
    end

    properties
        nComponents = 3;   % The number of principle components
    end
    properties

        means = [];           % A vector of the means
        pcaVectors = [];      % The PCA vectors.

        trainingTotalVariance = []; % The total variance contained in the
        % training data
        totalVariance = []; % The variance contained in the reduced
        % dimension data.
        totalVarianceCumulative = []; % The variance contained in the
        % reduced dimension data as a
        % function of the number of
        % components
        totalPercentVarianceCumulative = []; %The perceont of the total training variance explained in totalVarianceCumulative
    end
    properties (Hidden)
        removeMean = true;
    end

    methods

        % Allow for string, value pairs
        function self = prtPreProcPca(varargin)
            self = prtUtilAssignStringValuePairs(self,varargin{:});
        end
    end

    methods

        function dataSet = approximate(self,dataSet)
            % dataSetApp = approximate(pca,dataSet)
            %  Generate the PCA basis approximation to the data in dataSet.
            %
            dataSetScores = self.run(dataSet);
            dataSet = reconstruct(self,dataSetScores);
        end

        function dataSet = reconstruct(self,dataSetScores)
            % dataSetApp = reconstruct(pca,dataSetScores)
            %  Generate the PCA basis approximation using the scores in dataSetScores
            %

            xOut = repmat(self.means,dataSetScores.nObservations,1);
            if self.nComponents > 0
                xOut = xOut + (dataSetScores.X*self.pcaVectors');
            end
            dataSet = dataSetScores;
            dataSet.X = xOut;
        end

        function self = set.nComponents(self,nComp)
            if ~isnumeric(nComp) || ~isscalar(nComp) || nComp <= 0 || round(nComp) ~= nComp
                error('prt:prtPreProcPca','nComponents (%s) must be a positive scalar integer',mat2str(nComp));
            end
            self.nComponents = nComp;
        end

    end

    methods (Hidden = true)
        function featureNameModificationFunction = getFeatureNameModificationFunction(self) %#ok<MANU>
            featureNameModificationFunction = prtUtilFeatureNameModificationFunctionHandleCreator('PC Score #index#');
        end
    end

    methods (Access = protected, Hidden = true)
        function self = trainAction(self,dataSet)

            if self.removeMean
                self.means = prtUtilNanMean(dataSet.getObservations(),1);
            else
                self.means = zeros(1,dataSet.nFeatures);
            end

            if self.nComponents == 0
                return
            end
            x = bsxfun(@minus,dataSet.getObservations(),self.means);

            maxComponents = min(size(x));

            if self.nComponents > maxComponents
                warning('prt:prtPreProcPca','User specified # PCA components (%d) is > maximum number of PCA allowed (min(size(dataSet.data)) = %d)',self.nComponents,maxComponents);
                self.nComponents = maxComponents;
            end

            [s,u,v] = svds(x,self.nComponents); %#ok<ASGLU>

            self.pcaVectors = v;

            self.trainingTotalVariance = sum(var(x));
            pcaVariance = cumsum(var(x*v));

            self.totalVarianceCumulative = pcaVariance;
            self.totalVariance = self.totalVarianceCumulative(end);
            self.totalPercentVarianceCumulative = self.totalVarianceCumulative./self.trainingTotalVariance;
        end

        function dataSet = runAction(self,dataSet)
            dataSet.X = self.runActionFast(dataSet.getObservations);
        end

        function xOut = runActionFast(self,xIn)

            if self.nComponents == 0
                xOut = nan(size(xIn,1),1);
                return;
            end
            if self.removeMean
                xOut = bsxfun(@minus,xIn,self.means);
                if self.nComponents > 0
                    xOut = xOut*self.pcaVectors;
                end
            else
                xOut = xIn*self.pcaVectors;
            end
        end

    end
end


function b = iseven(x)
%ISEVEN True for even numbers.
%
%   ISEVEN(X) returns 1's where the elements of X are even numbers and 0's
%   where they are not.
%
%   See also ISINT, ISODD.

%   Author:      Peter J. Acklam
%   Time-stamp:  2003-11-03 18:55:21 +0100
%   E-mail:      pjacklam@online.no
%   URL:         http://home.online.no/~pjacklam

   error(nargchk(1, 1, nargin));
   if ~isnumeric(x)
      error('Argument must be a numeric array.');
   end

   cls = class(x);                      % class of input argument
   if isempty(x)
      b = feval(cls, x);                % return empty array of same class
   else
      switch cls
         case 'double'
            b = ~mod(x, 2);
         case 'single'
            % "mod" is not defined for class "single"; so convert input to double, compare,
            % and convert back
            b = single(~mod(double(x), 2));
         case {'uint8', 'uint16', 'uint32', 'uint64'}
            b = ~bitand(x, 1);
         case {'int8', 'int16', 'int32', 'int64'}
            error('Not implemented for classes int8, int16, int32, and int64.');
         otherwise
            error('Argument is of unrecognized class.');
      end
   end


function [sys,x0,str,ts]=Plant1_x1x2wt(t,x,u,flag)

switch flag,
    case 0,
        [sys,x0,str,ts]=mdlInitializeSizes;
    case 1,
        sys=mdlDerivatives(t,x,u);
    case 3,
        sys=mdlOutputs(x);
    case {2,4,9},
        sys=[];
    otherwise
        error(['Unhandled flag=',num2str(flag)]);
end
function [sys,x0,str,ts]=mdlInitializeSizes
    sizes=simsizes;
    sizes.NumContStates=2;
    sizes.NumDiscStates=0;
    sizes.NumOutputs=1;
    sizes.NumInputs=1;
    sizes.DirFeedthrough=1;
    sizes.NumSampleTimes=1;
    sys=simsizes(sizes);
    x0=[0;0];
    str=[];
    ts=[0 0];
function sys=mdlDerivatives(t,x,u)
    sys(1)=x(2);
    sys(2)=-5*x(1)-20*x(2)++0.5*sign(sin(t))+u;
function sys=mdlOutputs(x)
    sys=x(1);


function g = p41_g ( n, x )

%*****************************************************************************80
%
%% P41_G evaluates the gradient for problem 41.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 October 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the number of variables.
%
%    Input, real X(N), the values of the variables.
%
%    Output, real G(N), the gradient of the objective function.
%
  g = zeros ( 3, 1 );

  g(1) = 400.0 * x(1)^3 - 400.0 * x(2) * x(1) ...
    + 2.0 * x(1) - 2.0;

  g(2) = -200.0 * x(1)^2 + 220.2 * x(2) + 19.8 * x(4) - 40.0;

  g(3) = -360.0 * x(3) * x(4) + 360.0 * x(3)^3 ...
    + 2.0 * x(3) - 2.0;

  g(4) = + 180.0 * x(4) - 180.0 * x(3)^2 + 20.2 * x(4) ...
    + 19.8 * x(2) - 40.0;

  return
end


% book : Signals and Systems Laboratory with MATLAB
% authors : Alex Palamides & Anastasia Veloni

% Discrete Fourier Transform  properties

% Parseval's Theorem

n=0:10;
x=1./(n+1);
En=sum(abs(x).^2)

N=length(x);
X=dft(x);
Edft=(1/N)*sum(abs(X).^2)


%ASSEMMAT Assemble monolithic matrix.
%
%   [ A, T_A, T_SP ] = ASSEMMAT( PROB, S_A, I_CUB, N_CMAX, F_SPARSE, I_HRZ, SOLCOMP )
%
%   Assemble monolithic (coupled) matrix (for all dependent variables)
%   for the field S_A in the finite element data struct PROB. S_A can
%   be either "m" or "a", designating assembly of the mass matrix
%   defined in PROB.EQN.M, or system matrix defined in PROB.EQN.A.
%
%       Input       Value/[Size]           Description
%       -----------------------------------------------------------------------------------
%       prob        struct                 FEA problem data struct
%       s_a         char    {m/a}          Matrix field to assemble (mass/system)
%       i_cub       scalar  {2}            Numerical integration rule
%       n_cmax      scalar  {50000}        Max number of cells to assemble
%                                          for at once (to limit memory consumption)
%       f_sparse    logical {true}         Return sparse/struct matrix format
%       i_hrz       logical {false}        Apply HRZ diagonal (mass) lumping
%       solcomp     {all dvars/subd}       Dependent variables/subdomains to assemble for
%                                                                                .
%       Output      Value/[Size]           Description
%       -----------------------------------------------------------------------------------
%       A           sparse/struct [n_A]    Assembled system matrix
%       t_a         scalar                 Time spent assembling matrix
%       t_sp        scalar                 Time for sparse matrix conversion
%
%   See also ASSEMBLEA, ASSEMBLEPROB

% Copyright 2013-2022 Precise Simulation, Ltd.


%NLABELD Return numeric labels of classified dataset (c
%
% 	NLABELS = NLABELD(Z)
% 	NLABELS = Z*NLABELD
%	  NLABELS = NLABELD(A,W)
% 	NLABELS = A*W*NLABELD
%
% INPUT
%		Z        Classified dataset, or
%		A,W      Dataset and classifier mapping
%
% OUTPUT
%		NLABELS	 Column vector of numeric labels)
%
% DESCRIPTION
% Returns the numberic labels of the classified dataset Z (typically the
% result of a mapping or classification A*W). For each object in Z (i.e.
% each row) the feature label or class label (i.e. the column label) of the
% maximum column value is returned. This corresponds with the classes
% stored in W, which can be found by GETLABELS(W).
%
% SEE ALSO (PRTools Guide)
% MAPPINGS, DATASETS, TESTC, PLOTC, GETLABELS

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

function labels = nlabeld(a,w)

		if (nargin == 0)

		% Untrained mapping.
		labels = prmapping(mfilename,'fixed');

	elseif (nargin == 1)

		% In a classified dataset, the feature labels contain the output
		% of the classifier.
		[m,k] = size(a); featlist = getfeatlab(a);

		if (k == 1)
			% If there is one output, assume it's a 2-class discriminant:
			% decision boundary = 0.
			J = 2 - (double(a) >= 0);
		else
			% Otherwise, pick the column containing the maximum output.
			[dummy,J] = max(+a,[],2);
		end
		labels = J;
	elseif (nargin == 2)

		% Just construct classified dataset and call again.
		labels = feval(mfilename,a*w);

	else
		error ('too many arguments');
	end

return


% Mathematics Q3079400
% https://math.stackexchange.com/questions/3079400
% Numerical Implementation: Solution for the Euler Lagrange Equation Of the Rudin Osher Fatemi (ROF) Total Variation Denoising Model
% References:
%   1.  aa
% Remarks:
%   1.  sa
% TODO:
% 	1.  ds
% Release Notes
% - 1.0.000     29/03/2019
%   *   First release.

%% General Parameters

subStreamNumberDefault = 79;

run('InitScript.m');

figureIdx           = 0; %<! Continue from Question 1
figureCounterSpec   = '%04d';

generateFigures = ON;

%% Parameters

numRows = 5;
numCols = 4;

%% Load / Generate Data

mX = reshape(1:(numRows * numCols), numRows, numCols);

%% Gradient by MATLAB Notation

vDv = mX(1:end - 1, :) - mX(2:end, :);
vDv = vDv(:);
vDh = mX(:, 1:end - 1) - mX(:, 2:end);
vDh = vDh(:);

vDRef = [vDv; vDh];

%% Gradient by MATLAB Matrix Operation

mD = CreateGradientOperator(numRows, numCols);
vD = mD * mX(:);

%% Analysis

vE = abs(vD - vD);
max(vE)

%% Restore Defaults

% set(0, 'DefaultFigureWindowStyle', 'normal');
% set(0, 'DefaultAxesLooseInset', defaultLoosInset);


function [ points_lat, points_long ] = gen_hyperbola( doa_meters, rx1_lat, rx1_long, rx2_lat, rx2_long, geo_ref_lat, geo_ref_long)
%gen_hyperbola: calculates the points of a hyperbola from receiver positions
%               and the doa in meters

    % convert to xy coordinates
    [rx1_x, rx1_y] = latlong2xy(rx1_lat, rx1_long, geo_ref_lat, geo_ref_long);
    [rx2_x, rx2_y] = latlong2xy(rx2_lat, rx2_long, geo_ref_lat, geo_ref_long);

    % mit Kosinussatz Dreieck berechnen
    rx_x_dist = rx2_x - rx1_x;
    rx_y_dist = rx2_y - rx1_y;

    dist_12 = abs (rx_x_dist+i*rx_y_dist);  % positions in complex plane
    angle_12 = angle (rx_x_dist+i*rx_y_dist); % -pi to +pi

    hyp_x = zeros(1,1);
    hyp_y = zeros(1,1);

    hyp_x_leg1 = zeros(1,1);
    hyp_y_leg1 = zeros(1,1);
    hyp_x_leg2 = zeros(1,1);
    hyp_y_leg2 = zeros(1,1);
    hyp_point_counter = 0;

    if abs(doa_meters/1000) > dist_12
        disp(['TODA delay (' num2str(doa_meters) ' meters) larger than RX distance (' num2str(1000*  dist_12) ' meters) -> no solution possible ']);
        doa_meters = sign(doa_meters) * 0.995 * dist_12 * 1000;
        disp(['ATTENTION: Correcting TODA delay to 0.995 * RX distance (maximum possible value) = ' num2str(0.995*doa_meters) '']);
    end

    if abs(doa_meters/1000) <= dist_12

        %for r_1 = (exp(0:0.05:4)-1) / 5
        for r_1 = 0:0.05:10
            r_2 = r_1 - doa_meters/1000;
            %disp(['r_1 = ' num2str(r_1) ', r_2 = ' num2str(r_2)]);

            if ((r_2 + r_1) > dist_12)  % checks if triangle can be created

				acos_argument = (r_2^2 - r_1^2 - dist_12^2) / (-2*r_1*dist_12);

				if (acos_argument >= -1) && (acos_argument <= +1) % checks if triangle can be created

					hyp_point_counter = hyp_point_counter + 1;

					hyp_angle = acos(acos_argument); % inner angle of triangle at RX1

					abs_angle1 = wrap2pi(angle_12 + hyp_angle);  % 1st solution: hyperbola leg 1
					hyp_x_leg1(hyp_point_counter) = rx1_x + r_1 * cos(abs_angle1);
					hyp_y_leg1(hyp_point_counter) = rx1_y + r_1 * sin(abs_angle1);

					abs_angle2 = wrap2pi(angle_12 - hyp_angle);  % 2nd solution: hyperbola leg 2
					hyp_x_leg2(hyp_point_counter) = rx1_x + r_1 * cos(abs_angle2);
					hyp_y_leg2(hyp_point_counter) = rx1_y + r_1 * sin(abs_angle2);
                else
                    %disp(['acos argument ' num2str(acos_argument)]);
				end
            end

        end
    else
        disp('TODA delay larger than RX distance -> no solution possible');
    end

    if (hyp_point_counter == 0)
        disp('Hyperbola could not be constructed');
    end

    hyp_x = [fliplr(hyp_x_leg1) hyp_x_leg2];
    hyp_y = [fliplr(hyp_y_leg1) hyp_y_leg2];
    hyp_points = 2* hyp_point_counter;

    points_lat = zeros(hyp_points,1);
    points_long = zeros(hyp_points,1);

    for ii=1:1:hyp_points
        [points_lat(ii), points_long(ii)] = xy2latlong(hyp_x(ii), hyp_y(ii), geo_ref_lat, geo_ref_long);
    end

    disp(['Hyperbola with totally ' num2str(hyp_points) ' points generated.']);
end