classdef CEC2013_F2 < PROBLEM
% <single> <real> <large>
% Shifted Rastrigin's function

%------------------------------- Reference --------------------------------
% X. Li, K. Tang, M. N. Omidvar, Z. Yang, and K. Qin, Benchmark functions
% for the CEC'2013 special session and competition on large-scale global
% optimization, RMIT University, Australia, 2013.
%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

    properties
        Xopt;	% Optimal decision vector
    end
    methods
        %% Default settings of the problem
        function Setting(obj)
            CallStack = dbstack('-completenames');
            load(fullfile(fileparts(CallStack(1).file),'CEC2013.mat'),'Data');
            obj.Xopt = Data{2};
            obj.M    = 1;
            obj.D    = 1000;
            obj.lower    = zeros(1,obj.D) - 5;
            obj.upper    = zeros(1,obj.D) + 5;
            obj.encoding = ones(1,obj.D);
        end
        %% Calculate objective values
        function PopObj = CalObj(obj,PopDec)
            PopObj = Rastrigin(Tdiag(Tasy(Tosz(PopDec-repmat(obj.Xopt,size(PopDec,1),1)),0.2),10));
        end
    end
end

function F = Rastrigin(X)
    F = sum(X.^2-10*cos(2*pi*X)+10,2);
end

function Z = Tosz(X)
    X1 = zeros(size(X));
    X1(X~=0) = log(abs(X(X~=0)));
    C1 = zeros(size(X)) + 5.5;
    C1(X>0) = 10;
    C2 = zeros(size(X)) + 3.1;
    C2(X>0) = 7.9;
    Z = sign(X).*exp(X1+0.049*(sin(C1.*X1)+sin(C2.*X1)));
end

function Z = Tasy(X,beta)
    Z = X.^(1+repmat(beta*linspace(0,1,size(X,2)),size(X,1),1).*sqrt(X));
    Z(X<=0) = X(X<=0);
end

function Z = Tdiag(X,alpha)
    Z = X.*repmat(sqrt(alpha).^linspace(0,1,size(X,2)),size(X,1),1);
end


function Gxy = femGreenKernel(X,Y,green,k)
%+========================================================================+
%|                                                                        |
%|              OPENFEM - LIBRARY FOR FINITE ELEMENT METHOD               |
%|           openFem is part of the GYPSILAB toolbox for Matlab           |
%|                                                                        |
%| COPYRIGHT : Matthieu Aussal & Francois Alouges (c) 2017-2018.          |
%| PROPERTY  : Centre de Mathematiques Appliquees, Ecole polytechnique,   |
%| route de Saclay, 91128 Palaiseau, France. All rights reserved.         |
%| LICENCE   : This program is free software, distributed in the hope that|
%| it will be useful, but WITHOUT ANY WARRANTY. Natively, you can use,    |
%| redistribute and/or modify it under the terms of the GNU General Public|
%| License, as published by the Free Software Foundation (version 3 or    |
%| later,  http://www.gnu.org/licenses). For private use, dual licencing  |
%| is available, please contact us to activate a "pay for remove" option. |
%| CONTACT   : matthieu.aussal@polytechnique.edu                          |
%|             francois.alouges@polytechnique.edu                         |
%| WEBSITE   : www.cmap.polytechnique.fr/~aussal/gypsilab                 |
%|                                                                        |
%| Please acknowledge the gypsilab toolbox in programs or publications in |
%| which you use it.                                                      |
%|________________________________________________________________________|
%|   '&`   |                                                              |
%|    #    |   FILE       : femGreenKernel.m                              |
%|    #    |   VERSION    : 0.55                                          |
%|   _#_   |   AUTHOR(S)  : M. Aussal & F. Alouges & M. Averseng          |
%|  ( # )  |   CREATION   : 14.03.2017                                    |
%|  / 0 \  |   LAST MODIF : 01.05.2019                                    |
%| ( === ) |   SYNOPSIS   : Usefull green kernel functions                |
%|  `---'  |                                                              |
%+========================================================================+

% Security
if (size(X,2) ~= 3) || (size(Y,2) ~= 3)
    error('femGreenKernel.m : unavailable case')
end
if isempty(k)
    k = 0;
end

% Distances between particles
Rxy = sqrt( ...
    (X(:,1) - Y(:,1)).^2 + ...
    (X(:,2) - Y(:,2)).^2 + ...
    (X(:,3) - Y(:,3)).^2 );

% For empty wave-number
if isempty(k)
    k = 0;
end

% Green kernel definition
if strcmp(green,'[1/r]')
    Gxy = 1./Rxy;

elseif strcmp(green,'dx[1/r]')
    Gxy = - 1 ./ (Rxy.^3);

elseif strcmp(green,'dy[1/r]')
    Gxy = 1 ./ (Rxy.^3);

elseif strcmp(green,'[exp(ikr)/r]')
    Gxy = exp(1i*k*Rxy)./Rxy;

elseif strcmp(green,'dx[exp(ikr)/r]')
    Gxy = (1i*k - 1./Rxy) .* exp(1i*k.*Rxy) ./ (Rxy.^2);

elseif strcmp(green,'dy[exp(ikr)/r]')
    Gxy = - (1i*k - 1./Rxy) .* exp(1i*k.*Rxy) ./ (Rxy.^2);

elseif strcmp(green(1:end-1),'gradx[1/r]')
    j = str2double(green(end));
    Gxy = - (X(:,j)-Y(:,j)) ./ (Rxy.^3);

elseif strcmp(green(1:end-1),'grady[1/r]')
    j = str2double(green(end));
    Gxy = (X(:,j)-Y(:,j)) ./ (Rxy.^3);

elseif strcmp(green(1:end-1),'gradx[exp(ikr)/r]')
    j = str2double(green(end));
    Gxy = (1i*k - 1./Rxy) .* exp(1i*k.*Rxy) .* ...
        (X(:,j)-Y(:,j)) ./ (Rxy.^2);

elseif strcmp(green(1:end-1),'grady[exp(ikr)/r]')
    j = str2double(green(end));
    Gxy = - (1i*k - 1./Rxy) .* exp(1i*k.*Rxy) .* ...
        (X(:,j)-Y(:,j)) ./ (Rxy.^2);

elseif strcmp(green,'[log(r)]')
    Gxy = log(Rxy);

elseif strcmp(green,'[H0(kr)]')
    Gxy = besselh(0,k*Rxy);

elseif strcmp(green(1:end-1),'gradx[log(r)]')
    j = str2double(green(end));
    Gxy = (X(:,j)-Y(:,j)) ./ (Rxy.^2);

elseif strcmp(green(1:end-1),'grady[log(r)]')
    j = str2double(green(end));
    Gxy = - (X(:,j)-Y(:,j)) ./ (Rxy.^2);

elseif strcmp(green(1:end-1),'gradx[H0(kr)]')
    j   = str2double(green(end));
    Gxy = - k * besselh(1,k*Rxy) .* (X(:,j)-Y(:,j)) ./ Rxy;

elseif strcmp(green(1:end-1),'grady[H0(kr)]')
    j   = str2double(green(end));
    Gxy = k * besselh(1,k*Rxy) .* (X(:,j)-Y(:,j)) ./ Rxy;

elseif strcmp(green(1:end-2),'[ij/r+rirj/r^3]')
    i = str2double(green(end-1));
    j = str2double(green(end));
    Gxy = (i==j)./Rxy + (X(:,i)-Y(:,i)).*(X(:,j)-Y(:,j))./(Rxy.^3);

elseif strcmp(green(1:end-3),'[rirjrk/r^5]')
    i = str2double(green(end-2));
    j = str2double(green(end-1));
    k = str2double(green(end));
    Gxy = (X(:,i)-Y(:,i)).*(X(:,j)-Y(:,j)).*(X(:,k)-Y(:,k))./(Rxy.^5);

else
    error('Error in femGreenKernel.m : unknown green kernel')
end

% Singularity
if strcmp(green,'[exp(ikr)/r]')
    Gxy(Rxy<1e-12) = 0 + 1i*k;
elseif strcmp(green,'[H0(kr)]')
    gamma         = 0.5772156649;
    Gxy(Rxy<1e-12) = 1 + 1i*(2/pi*(gamma+log(k/2)));
else
    Gxy(Rxy<1e-12) = 0;
end
end


function pval = lme_mass_LR(statsfull,statsred,q)
% pval = lme_mass_LR(statsfull,statsred,q)
%
% Likelihood ratio test for the random effects. It can be used to test if a
% model with q+1 random effects is significantly better than a model with q
% random effects.
%
% Input
% statsfull: Structure array containing statistiscs for every voxel/vertex
% (see lme_FSfit for more details on these statistics) for the full
% model (the one with q+1 random effects).
% statsred: Structure array containing statistiscs for every voxel/vertex
% for the reduced model (the one with q random effects).
% q: Number of random effects in the reduced model.
%
% Output
% pval: P-value of the test at each voxel/vertex (based on a 50:50 mixture
% of chi-squared distributions with q and q+1 degrees of freedom).
%
% $Revision: 1.2 $  $Date: 2015/01/06 17:14:55 $
% Original Author: Jorge Luis Bernal Rusiel
% CVS Revision Info:
%    $Author: mreuter $
%    $Date: 2015/01/06 17:14:55 $
%    $Revision: 1.2 $
% References: Bernal-Rusiel J.L., Greve D.N., Reuter M., Fischl B., Sabuncu
% M.R., 2012. Statistical Analysis of Longitudinal Neuroimage Data with Linear
% Mixed Effects Models, NeuroImage, doi:10.1016/j.neuroimage.2012.10.065.
%

if nargin < 3
    error('Too few inputs');
end;

nv = length(statsfull);
pval = zeros(1,nv);
for i=1:nv
    lrstats = lme_LR(statsfull(i).lreml,statsred(i).lreml,q);
    pval(i) = lrstats.pval;
end;


%%*******************************************************************
%% randinfsdp.m : creates random infeasible SDP problems with various block
%%                diagonal structures.
%%
%% [blk,Avec,C,b,X0,y0,Z0] =
%%      randinfsdp(dense_blk,sparse_blk,diag_blk,m,infeas,solve);
%%
%% E.g.
%%      randinfsdp([32 20],[10 5],100,10,infeas,solve);
%%
%%  dense_blk : for generating dense blocks, where
%%              dense_blk(i) is the dimension of the ith block
%%  sparse_blk: for generating a sparse block of small subblocks, where
%%              sparse_blk(i) is the size of the ith subblock.
%%  diag_blk: for generating a column vector of length specified by
%%            diag_blk (this corresponds to a diagonal block).
%%
%%  infeas = 1 if want primal infeasible pair of problems
%%         = 2 if want dual infeasible pair of problems
%%
%%  solve = 0 just to initialize
%%        = 1 if want to solve the problem.
%%
%% SDPT3: version 3.0
%% Copyright (c) 1997 by
%% K.C. Toh, M.J. Todd, R.H. Tutuncu
%% Last modified: 2 Feb 01
%%******************************************************************

   function  [blk,Avec,C,b,X0,y0,Z0] = ...
                randinfsdp(dense_blk,sparse_blk,diag_blk,m,infeas,solve);

   if nargin < 6; solve = 0; end;
   if nargin < 5; error(' insufficient number of inputs '); end;
   if all(infeas-[1 2]);
      error(' infeas must be 1 or 2 ');
   end

   blk = [];
   if ~isempty(dense_blk);
      for k = 1:length(dense_blk);
          blk{k,1} = 's';  blk{k,2} = dense_blk(k);
      end;
   end;
   if ~isempty(sparse_blk);
      if size(sparse_blk,1) > size(sparse_blk,2); sparse_blk = sparse_blk'; end;
      tmp = size(blk,1);
      blk{tmp+1,1} = 's';
      blk{tmp+1,2} = sparse_blk;
   end;
   if ~isempty(diag_blk);
      tmp = size(blk,1);
      blk{tmp+1,1} = 'l';
      blk{tmp+1,2} = diag_blk;
   end;
   N = size(blk,1);

   if (infeas == 1),
%%
%% primal infeasible
%%
%% generate infeasibility certificate Zi and Z0
%%
   tmp_sp = sparse(sum(sparse_blk));
   for L = 1:2,
       T = [];
       if ~isempty(dense_blk);
          for k = 1:length(dense_blk);
              n = dense_blk(k);
              tmp = randn(n);
              tmp = tmp*tmp';
              tmp = 0.5*(tmp + tmp');
              %mineig = min(real(eig(tmp)));
              %if (mineig < 0); tmp = tmp - 1.1*mineig*eye(n); end;
              T{k,1} = tmp;
          end;
       end;
       if ~isempty(sparse_blk);
          for k = 1:length(sparse_blk);
              n = sparse_blk(k);
              pos = [sum(sparse_blk(1:k-1))+1 : sum(sparse_blk(1:k))];
              tmp = randn(n);
              tmp = tmp*tmp';
              tmp = 0.5*(tmp + tmp');
              %mineig = min(real(eig(tmp)));
              %if (mineig < 0); tmp = tmp - 1.1*mineig*eye(n); end;
              tmp_sp(pos,pos) = sparse(tmp);
          end;
          T{size(T,1)+1,1} = tmp_sp;
       end;
       if ~isempty(diag_blk);
          tmp = randn(diag_blk,1);
          tmp = tmp.*tmp;
          %mineig = min(tmp);
          %if (mineig < 0); tmp = tmp - 1.1*mineig*ones(diag_blk,1); end;
          T{size(T,1)+1,1} = tmp;
       end;
       if (L == 1);  Zi = T; else; Z0 = T; end;
   end;
%%
%% set up the matrices Ak and b
%%
   A  = cell(N,m);
   Ak_sp = sparse(sum(sparse_blk),sum(sparse_blk));
   for k = 1:m;
       Ak = [];
       if ~isempty(dense_blk);
          for j = 1:length(dense_blk);
              tmp = randn(dense_blk(j)); tmp = 0.5*(tmp+tmp');
              Ak{j,1} = tmp;
          end;
       end;
       if ~isempty(sparse_blk);
          for j = 1:length(sparse_blk);
              n = sparse_blk(j);
              tmp = randn(n); tmp = 0.5*(tmp+tmp');
              pos = [sum(sparse_blk(1:j-1))+1 : sum(sparse_blk(1:j))];
              Ak_sp(pos,pos) = tmp;
          end;
          Ak{size(Ak,1)+1,1} = Ak_sp;
       end;
       if ~isempty(diag_blk);
          Ak{size(Ak,1)+1,1} = randn(diag_blk,1);
       end;
       A(:,k) = Ak;
   end;

   y = ones(m,1);
   SAZm = ops(ops(Zi,'+',Asum(blk,A,y)),'/',m);
   SAZm = ops(ops(SAZm,'+',ops(SAZm,'transpose')),'*',0.5);
   yi  = randn(m,1);
   for k = 1:m,
      Ak = A(:,k);
      Ak = ops(ops(Ak,'-',SAZm),'/',yi(k));
      Ak = ops(ops(Ak,'+',ops(Ak,'transpose')),'*',0.5);
      A(:,k) = Ak;
   end;
   b = randn(m,1);
   if (b'*yi < 0), b = -b; end;
   y0  = randn(m,1);
   C = ops(Z0,'+',Asum(blk,A,y0));
   C = ops(ops(C,'+',ops(C,'transpose')),'*',0.5);
%%
%% (yi,Zi) is a primal infeasibility certificate
%%
   elseif (infeas == 2),
%%
%% dual infeasible
%%
%% generate infeasibility certificate Xi, positive definite X0, and C
%%
   tmp_sp_Xi  = sparse(sum(sparse_blk));
   tmp_sp_X0 = sparse(sum(sparse_blk));
   tmp_sp_C   = sparse(sum(sparse_blk));
   Xi = [];
   X0 = [];
   C  = [];
       if ~isempty(dense_blk);
          for k = 1:length(dense_blk);
              n = dense_blk(k);
              tmp = randn(n);
              tmp = tmp*tmp';
              tmp = 0.5*(tmp + tmp');
              %mineig = min(real(eig(tmp)));
              %if (mineig < 0); tmp = tmp - 1.1*mineig*eye(n); end;
              Xi{k,1} = tmp;
              tmp = randn(n);
              tmp = tmp*tmp';
              tmp = 0.5*(tmp + tmp');
              %mineig = min(real(eig(tmp)));
              %if (mineig < 0); tmp = tmp - 1.1*mineig*eye(n); end;
              X0{k,1} = tmp;
              tmp = randn(n); tmp = 0.5*(tmp + tmp');
              C{k,1} = tmp;
          end;
       end;
       if ~isempty(sparse_blk);
          for k = 1:length(sparse_blk);
              n = sparse_blk(k);
              pos = [sum(sparse_blk(1:k-1))+1 : sum(sparse_blk(1:k))];
              tmp = randn(n);
              tmp = tmp*tmp';
              tmp = 0.5*(tmp + tmp');
              %mineig = min(real(eig(tmp)));
              %if (mineig < 0); tmp = tmp - 1.1*mineig*eye(n); end;
              tmp_sp_Xi(pos,pos) = sparse(tmp);
              tmp = randn(n);
              tmp = tmp*tmp';
              tmp = 0.5*(tmp + tmp');
              %mineig = min(real(eig(tmp)));
              %if (mineig < 0); tmp = tmp - 1.1*mineig*eye(n); end;
              tmp_sp_X0(pos,pos) = sparse(tmp);
              tmp = randn(n);  tmp = 0.5*(tmp + tmp');
              tmp_sp_C(pos,pos) = sparse(tmp);
          end;
          Xi{size(Xi,1)+1,1} = tmp_sp_Xi;
          X0{size(X0,1)+1,1} = tmp_sp_X0;
          C{size(C,1)+1,1}   = tmp_sp_C;
       end;
       if ~isempty(diag_blk);
          n = diag_blk;
          tmp = randn(n,1);
          tmp = tmp.*tmp;
          %mineig = min(tmp);
          %if (mineig < 0); tmp = tmp - 1.1*mineig*ones(n,1); end;
          Xi{size(X0,1)+1,1} = tmp;
          tmp = randn(n,1);
          tmp = tmp.*tmp;
          %mineig = min(tmp);
          %if (mineig < 0); tmp = tmp - 1.1*mineig*ones(n,1); end;
          X0{size(X0,1)+1,1} = tmp;
          tmp = randn(n,1);
          C{size(C,1)+1,1} = tmp;
       end;
%%
%% set up the matrices Ak and b
%%
   trXX = blktrace(blk,Xi,Xi);
   A  = cell(N,m);
   Ak_sp = sparse(sum(sparse_blk),sum(sparse_blk));
   b = ones(m,1);
   for k = 1:m;
       Ak = [];
       if ~isempty(dense_blk);
          for j = 1:length(dense_blk);
              n = dense_blk(j);
              Aj  = randn(n);
              Ak{j,1} = (Aj + Aj')/2;
          end;
       end;
       if ~isempty(sparse_blk);
          for j = 1:length(sparse_blk);
              pos = [sum(sparse_blk(1:j-1))+1 : sum(sparse_blk(1:j))];
              n = sparse_blk(j);
              Aj  = randn(n);
              Ak_sp(pos,pos) = (Aj + Aj')/2;
          end;
          Ak{size(Ak,1)+1,1} = Ak_sp;
       end;
       if ~isempty(diag_blk);
          n = diag_blk;
          Ak{size(Ak,1)+1,1} = randn(n,1);
       end;
       trAX = blktrace(blk,Ak,Xi);
       Ak = ops(Ak,'-',ops(Xi,'*',(trAX/trXX)));
       Ak = ops(ops(Ak,'+',ops(Ak,'transpose')),'*',0.5);
       A(:,k) = Ak;
       b(k) = blktrace(blk,Ak,X0);
   end;
   if (blktrace(blk,C,Xi) > 0), C = ops(C,'*',-1); end;
%%
%% Xi is a dual infeasibility certificate
%%
   end;

%%
%% infeasible initial iterate
%%
   Avec = svec(blk,A,ones(size(blk,1),1));
   [X0,y0,Z0] = infeaspt(blk,Avec,C,b);
%%
   if solve;
      [obj,X,y,Z] = sqlp(blk,Avec,C,b,[],X0,y0,Z0);
   end;

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


function plot_bootfitloglike_a2(mycat,time,timef,bootloops,maepi)
    % Plots Ncum (observed vs. modeled) for specified time windows (choose model for the learning period)
    %
    % plot_bootfitloglike_a2(mycat,time,timef,bootloops,ZG.maepi);
    % --------------------------------------------------
    % Plots Ncum observed vs. Ncum modeled for specified time windows
    % with choosing the model for the learning period
    % Input variables:
    % mycat         : earthquake catalog
    % time      : learning period fo fit Omori parameters [days]
    % timef     : forecast period [days]
    % bootloops : Number of bootstraps
    % ZG.maepi     : mainshock
    %
    % J.Woessner, S. Wiemer

%FIXME mCat ZG.maepi still treated as arrays
report_this_filefun();
    % Surpress warnings from fmincon
    % warning off;

    if ~ensure_mainshock()
        return
    end
    date_matlab = datenum(mycat.Date);
    date_main = datenum(ZG.maepi.Date);
    time_aftershock = date_matlab-date_main;

% Select biggest aftershock earliest in time, but more than 1 day after mainshock
    fDay = 1; %days
    vSel = (mycat.Date > ZG.maepi.Date + days(fDay)) & mycat.Date<= ZG.maepi.Date+days(time);
    mCat = mycat.subset(vSel);
    vSel = mCat.Magnitude == max(mCat.Magnitude);
    vBigAf = mCat(vSel,:);
    if sum(vSel) > 1
        vBigAf.sort('Date')
        vBigAf = vBigAf.subset(1);
    end
    date_biga = datenum(vBigAf.Date);
    fT1 = date_biga - date_main; % Time of big aftershock

    % Aftershock times
    l = time_aftershock(:) > 0;
    tas = time_aftershock(l);
    eqcatalogue = mycat.subset(l);

    % time_as: Learning period
    l = tas <= time;
    time_as=tas(l);

    % Times up to the forecast time
    lf = tas <= time+timef ;
    time_asf= [tas(lf) ];
    time_asf=sort(time_asf);

    % % Calculate p,c,k for dataset
    % prompt  = {'Enter model number (1:pck, 2:pckk, 3:ppckk, 4:ppcckk:'};
    % title   = 'Model selection for fitting aftershock sequence';
    % lines= 1;
    % def     = {'1'};
    % answer  = inputdlg(prompt,title,lines,def);
    % nMod = str2double(answer{1});
    % Calculate fits of different models
    % see OmoriModel for enumaration details

    MOL_models = sort(enumeration('OmoriModel'));

    mRes = [];
    % Modified Omori law (pck)

    for nMod=1:numel(MOL_models) % do this for each model
        [pval1, pval2, cval1, cval2, kval1, kval2, fAIC, fL] = bruteforceloglike_a2(time_as, fT1, MOL_models(nMod));
        mRes(nMod,:) = [nMod, pval1, pval2, cval1, cval2, kval1, kval2, fAIC, fL];
    end

    % Select best fitting model by AIC
    vSel = (mRes(:,8)==min(mRes(:,8)));
    mRes = mRes(vSel,:);
    if length(mRes(:,1)) > 1
        vSel = (mRes(:,1)==min(mRes(:,1)));
        mRes = mRes(vSel,:);
    end
    % Model to use for bootstrapping as of lowest AIC to observed data
    nMod = OmoriModel(mRes(1,1));
    pval1= mRes(1,2);
    pval2= mRes(1,3);
    cval1= mRes(1,4);
    cval2= mRes(1,5);
    kval1= mRes(1,6);
    kval2= mRes(1,7);

    % Calculate goodness of fit with KS-Test and RMS
    [H,P,KSSTAT,fRMS] = calc_llkstest_a2(time_as,fT1,pval1, pval2, cval1, cval2, kval1, kval2, nMod);

    % Calculate uncertainty and mean values of p,c,and k
    [mMedModF, mStdL, loopout] = brutebootloglike_a2(time_as, time_asf, bootloops,fT1,nMod);
    pmed1 = mMedModF(1,1);
    pmed2 = mMedModF(1,3);
    cmed1 = mMedModF(1,5);
    cmed2 = mMedModF(1,7);
    kmed1 = mMedModF(1,9);
    kmed2 = mMedModF(1,11);

    % Start plotting
    if (~isnan(pval1) && ~isnan(pval2))

        figure_w_normalized_uicontrolunits('Numbertitle','off','Name','Forecast aftershock occurence')
        loopout(:,end+1) = 0; % add a column

        pval1s = loopout(:,1);
        pval2s = loopout(:,2);
        cval1s = loopout(:,3);
        cval2s = loopout(:,4);
        kval1s = loopout(:,5);
        kval2s = loopout(:,6);

        cumnr_model = OmoriModel.doForecast(nMod, time_asf, pval1s, cval1s, kval1s, fT1, kval2s, pval2s, cval2s);

        loopout(:,9)=max(cumnr_model);

        pfloop = plot(time_asf,cumnr_model,'color',[0.8 0.8 0.8]);
        set(gca,'NextPlot','add')

        warning('This uses only the last values')
        % 2nd moment of bootstrap number of forecasted number of events
        fStdBst = std(loopout(:,9),1,'omitnan');
        %
        % Plot the forecast ...
        cumnrf = (1:length(time_asf))';

        cumnr_modelf = OmoriModel.doForecast(nMod, time_asf, pval1s(end), cval1s(end), kval1s(end), fT1, kval2s(end), pval2s(end), cval2s(end));

        time_asf=sort(time_asf);
        cumnr_modelf=sort(cumnr_modelf);

        pf1 =  plot(time_asf,cumnr_modelf,'g-.','Linewidth',2);
        set(gca,'NextPlot','add')
        %pf2 =  plot(time_asf,cumnrf, 'b-','Linewidth',2);
        %
        % Plot the  fit to the observed data
        % Cumulative number of observed events
        cumnr = (1:length(time_as))';
        cumnr_model = [];
        cumnr_model= OmoriModel.doForecast(nMod, time_as, pval1s(end), cval1s(end), kval1s(end), fT1, kval2s(end), pval2s(end), cval2s(end));

        time_as=sort(time_as);
        cumnr_model=sort(cumnr_model);
        p1 = plot(time_as,cumnr_model,'r','Linewidth',2,'Linestyle','--');
        set(gca,'NextPlot','add');
        p2 = plot(time_as,cumnr,'b','Linewidth',2,'Linestyle','--');

        % Plot the forecast from median value
        cumnr_modelmed = [];
        cumnr_modelmed= OmoriModel.doForecast(nMod, time_asf, pmed1, cmed1, kmed1, fT1, kmed2, pmed2, cmed2);

        time_asf=sort(time_asf);
        cumnr_modelmed=sort(cumnr_modelmed);
        pmedmod =  plot(time_asf,cumnr_modelmed,'y-.','Linewidth',2);

        % Plot observed events in forecast period from endpoint of modeled events in learning period
        vSel = time_asf >= max(time_as);
        vCumnr_forecast = cumnrf(vSel,:);
        vTime_forecast = time_asf(vSel,:);
        % Difference of modelled and observed number of events at time_as
        fDiff_timeas = cumnr_modelmed(length(time_as))-cumnrf(length(time_as));
        vCumnr_forecast = vCumnr_forecast+fDiff_timeas;
        pf3 = plot(vTime_forecast, vCumnr_forecast,'m-.','Linewidth',2);

        xlabel('Time [days]')
        ylabel('Cumulative number of aftershocks')
        xlim([0 max(time_asf)]);

        % Plot standard deviation from bootstrap
        ps2=errorbar(max(time_asf),max(cumnr_modelmed),fStdBst,fStdBst);
        set(ps2,'Linewidth',2,'Color',[1 0 0])
        %
        %     % Title
        % Find amount of events in forecast period for modeled data
        nummod2 = max(cumnr_modelf)-cumnr_modelf(length(time_as));
        nummod = max(cumnr_modelmed)-cumnr_modelmed(length(time_as));
        %     % Find amount of  events in forecast period for observed data
        l = time_asf <=time+timef & time_asf > time;
        numreal = sum(l); % observed number of aftershocks
        %     fRc_Flaw = (numreal-nummod)/sigma;
        fRc_Bst2 = (numreal-nummod2)/fStdBst;
        fRc_Bst = (numreal-nummod)/fStdBst;

        % Round values for output
        pval1 = round(pval1,2);
        pval2 = round(pval2,2);
        cval1 = round(cval1,3);
        cval2 = round(cval2,3);
        kval1 = round(kval1,1);
        kval2 = round(kval2,1);
        pmed1 = round(pmed1,2); mStdL(1,1) = round(mStdL(1,1),2);
        pmed2 = round(pmed2,2); mStdL(1,2) = round(mStdL(1,2),2);
        cmed1 = round(cmed1,3); mStdL(1,3) = round(mStdL(1,3),3);
        cmed2 = round(cmed2,3); mStdL(1,4) = round(mStdL(1,4),3);
        kmed1 = round(kmed1,1); mStdL(1,5) = round(mStdL(1,5),2);
        kmed2 = round(kmed2,1); mStdL(1,6)= round(mStdL(1,6),2);
        fAIC = round(fAIC,2);
        fStdBst = round(fStdBst,2);
        fRc_Bst = round(fRc_Bst,2);

        % Set line for learning period
        yy = get(gca,'ylim');
        plot([max(time_as) max(time_as)],[0 yy(2)],'k-.')
        textX = max(time_asf)*0.1;
        textY = @(z) yy(2)* z;

        switch nMod
            case OmoriModel.pck
                string1 = sprintf('p = %g; c = %g; k = %g', pval1, cval1, kval1);
                string3 = sprintf('pm = %g+-%g; cm = %g+-%g; km = %g+-%g', mped1, mStdL(1,1), cmed1, mStdL(1,3), kmed1, mStdL(1,5));
                % string3=['pm = ' num2str(pmed1) '+-' num2str(mStdL(1,1)) '; cm = ' num2str(cmed1) '+-' num2str(mStdL(1,3)) '; km = ' num2str(kmed1) '+-' num2str(mStdL(1,5))];
                text(textX,textY(0.9),string1,'FontSize',8);
                text(textX,textY(0.8),string3,'FontSize',8);
            case  OmoriModel.pckk
                string1 = sprintf('p = %g; c = %g; k1 = %g; k2 = %g', pval1, cval1, kval1, kval2);
                string3 = sprintf('pm = %g+-%g; cm = %g+-%g; km1 = %g+-%g; km2 = %g+-%g',...
                    mped1, mStdL(1,1), cmed1, mStdL(1,3), kmed1, mStdL(1,5), kmed2, mStdL(1,6));
                text(textX,textY(0.9),string1,'FontSize',8);
                text(textX,textY(0.8),string3,'FontSize',8);
            case OmoriModel.ppckk
                string1 = sprintf('p1 = %g; c = %g; k1 = %g', pval1, cval1, kval1);
                string2 = sprintf('p2 = %g; k2 = %g', pval2, kval2);
                string3 = sprintf('pm1 = %g+-%g; cm = %g+-%g; km1 = %g+-%g', mped1, mStdL(1,1), cmed1, mStdL(1,3), kmed1, mStdL(1,5));
                string4 = sprintf('pm2 = %g+-%g; km2 = %g+-%g', mped2, mStdL(1,2), kmed2, mStdL(1,6));
                text(textX,textY(0.9),string1,'FontSize',8);
                text(textX,textY(0.85),string2,'FontSize',8);
                text(textX,textY(0.8),string3,'FontSize',8);
                text(textX,textY(0.75),string4,'FontSize',8);
            case OmoriModel.ppcckk
                string1 = sprintf('p1 = %g; c1 = %g; k1 = %g', pval1, cval1, kval1);
                string2 = sprintf('p2 = %g; c2 = %g; k2 = %g', pval2, cval2, kval2);
                string3 = sprintf('pm1 = %g+-%g; cm1 = %g+-%g; km1 = %g+-%g', mped1, mStdL(1,1), cmed1, mStdL(1,3), kmed1, mStdL(1,5));
                string4 = sprintf('pm2 = %g+-%g; cm2 = %g+-%g; km2 = %g+-%g', mped2, mStdL(1,2), cmed2, mStdL(1,4), kmed2, mStdL(1,6));
                text(textX,textY(0.9),string1,'FontSize',8);
                text(textX,textY(0.85),string2,'FontSize',8);
                text(textX,textY(0.8),string3,'FontSize',8);
                text(textX,textY(0.75),string4,'FontSize',8);
        end
        string=sprintf('\sigma(Bst) = %g Rc = %g',fStdBst, fRc_Bst);%' Rc(Obfit) = ' num2str(fRc_Bst2)];
        text(textX,yy(2)*0.1,string,'FontSize',8);

        sAIC = sprintf('AIC = %g',fAIC);
        text(textX,yy(2)*0.05,sAIC,'FontSize',8);

        paf = plot(fT1, 0,'h','MarkerFaceColor',[1 1 0],'MarkerSize',12,'MarkerEdgeColor',[0 0 0] );
        sGoodfit = sprintf('KS Test: H = %g, KS statistic = %g P value = %g; RMS = %g', H, KSSTAG, P,fRMS);
        text(textX,yy(2)*0.15,sGoodfit,'FontSize',8);
        % Legend
        legend([p2 p1 pf1 pf3 pmedmod min(ps2) paf],'data','model to data','forecast','observed', 'Mean Bst-model', '\sigma (Bst)','Sec. AF', 'location', 'Best');
    else
        disp('no result')
    end


function area = sphericalCapsAreaC6(varargin)
%SPHERICALCAPSAREAC6 compute area of spherical caps on the unit sphere
%
%   AREA = sphericalCapsAreaC6
%   Compute the area of the spherical caps associated to the voronoi
%   diagram on the unit sphere, when the germs correspond to the 26
%   discrete directions in the unit cube.
%   Result is a 1x3 array, containing fraction of area of each type of
%   spherical cap.
%   area(1) concerns the direction [1 0 0]
%   area(2) concerns the direction [0 1 0]
%   area(3) concerns the direction [0 0 1]
%   Result is formatted such that 2*(area(1)+area(2)+area(3))=1.
%   For homogeneous lattices, all areas are equal.
%
%   AREA = sphericalCapsAreaC6(DELTA)
%   where DELTA = [DELTA1 DELTA2 DELTA3]  specifies the resolution of unit
%   voxel in each direction.
%
%
%   Algorithm :
%   - separate 3 basic case, corresponding on the type of directions
%   - for each cases, specify manually germ and neighbours
%   - compute great circle between each couple of germ
%   - compute intersection points of these circles
%   - sort intersection points around the central germ, giving a spherical
%      polygon
%   - compute triangulation of the resulting polygon
%   - compute spherical area of each triangle
%   - and area of each polygon
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 21/02/2005.
%

%   HISTORY
%   27/04/2007 extends to non uniform grid spacing

%% Initializations

% grid resolution
delta = [1 1 1];
if ~isempty(varargin)
    delta = varargin{1};
end

% poins in the 26 discrete directions
%pt000 = normalize([-1 -1 -1].*delta);
%pt100 = normalize([ 0 -1 -1].*delta);
%pt200 = normalize([+1 -1 -1].*delta);
%pt010 = normalize([-1  0 -1].*delta);
pt110 = normalize([ 0  0 -1].*delta);
% pt210 = normalize([+1  0 -1].*delta);
% pt020 = normalize([-1 +1 -1].*delta);
% pt120 = normalize([ 0 +1 -1].*delta);
% pt220 = normalize([+1 +1 -1].*delta);

%pt001 = normalize([-1 -1  0].*delta);
pt101 = normalize([ 0 -1  0].*delta);
% pt201 = normalize([+1 -1  0].*delta);
pt011 = normalize([-1  0  0].*delta);
%pt111 = [ 0  0  0];             % origin point
pt211 = normalize([+1  0  0].*delta);
% pt021 = normalize([-1 +1  0].*delta);
pt121 = normalize([ 0 +1  0].*delta);
% pt221 = normalize([+1 +1  0].*delta);

% pt002 = normalize([-1 -1 +1].*delta);
% pt102 = normalize([ 0 -1 +1].*delta);
% pt202 = normalize([+1 -1 +1].*delta);
% pt012 = normalize([-1  0 +1].*delta);
pt112 = normalize([ 0  0 +1].*delta);
% pt212 = normalize([+1  0 +1].*delta);
% pt022 = normalize([-1 +1 +1].*delta);
% pt122 = normalize([ 0 +1 +1].*delta);
% pt222 = normalize([+1 +1 +1].*delta);

% basic unit sphere
sphere = [0 0 0  1];

%% Spherical cap type 1 direction [1 0 0]
% ----------------------------------------------------------------------

% Compute area of voronoi cell for a point on the Ox axis, i.e. a point
% in the 6-neighboorhoud of the center.
centre1 = pt211;
% neighbours of chosen point, sorted by angle
%voisins1 = [pt200;pt201;pt202;pt212;pt222;pt221;pt220;pt210];
voisins1 = [pt112;pt121;pt110;pt101];
n1 = size(voisins1, 1);

% compute separating circles
planes1 = zeros(n1, 9);
for i=1:n1
    planes1(i,1:9) = normalizePlane(medianPlane(centre1, voisins1(i,:)));
%    circles1(i,1:7) = intersectPlaneSphere(planes1(i,:), sphere);
end

% compute circle intersections
lines1 = zeros(n1, 6);
for i=1:n1
    lines1(i,1:6) = intersectPlanes(planes1(i,:), ...
        planes1(mod(i,n1)+1,:));
    points1(2*i-1:2*i,1:3) = intersectLineSphere(lines1(i,:), sphere);
end

% keep only points with x>0
ind = dot(points1, repmat(centre1, [2*n1 1]), 2)>0;
points1 = points1(ind,:);
n1 = size(points1, 1);

% compute spherical area of each triangle [center  pt[i+1]%4   pt[i] ]
angles1 = zeros(n1, 1);
for i=1:n1
    pt1 = points1(i, :);
    pt2 = points1(mod(i  , n1)+1, :);
    pt3 = points1(mod(i+1, n1)+1, :);

    angles1(i) = sphericalAngle(pt1, pt2, pt3);
end

% compute area of spherical polygon
area1 = sum(angles1)-pi*(n1-2);

%% Spherical cap type 1 direction [0 1 0]
% ----------------------------------------------------------------------

% Compute area of voronoi cell for a point on the Oy axis, i.e. a point
% in the 6-neighboorhoud of the center.
centre1 = pt121;
% neighbours of chosen point, sorted by angle
%voisins1 = [pt200;pt201;pt202;pt212;pt222;pt221;pt220;pt210];
%voisins1 = [pt221;pt222;pt122;pt022;pt021;pt020;pt120;pt220];
voisins1 = [pt211;pt112;pt011;pt110];
n1 = size(voisins1, 1);

% compute separating circles
planes1 = zeros(n1, 9);
for i=1:n1
    planes1(i,1:9) = normalizePlane(medianPlane(centre1, voisins1(i,:)));
%    circles1(i,1:7) = intersectPlaneSphere(planes1(i,:), sphere);
end

% compute circle intersections
lines1 = zeros(n1, 6);
for i=1:n1
    lines1(i,1:6) = intersectPlanes(planes1(i,:), ...
        planes1(mod(i,n1)+1,:));
    points1(2*i-1:2*i,1:3) = intersectLineSphere(lines1(i,:), sphere);
end

% keep only points with x>0
ind = dot(points1, repmat(centre1, [2*n1 1]), 2)>0;
points1 = points1(ind,:);
n1 = size(points1, 1);

% compute spherical area of each triangle [center  pt[i+1]%4   pt[i] ]
angles1 = zeros(n1, 1);
for i=1:n1
    pt1 = points1(i, :);
    pt2 = points1(mod(i  , n1)+1, :);
    pt3 = points1(mod(i+1, n1)+1, :);

    angles1(i) = sphericalAngle(pt1, pt2, pt3);
end

% compute area of spherical polygon
area2 = sum(angles1)-pi*(n1-2);

%% Spherical cap type 1 direction [0 0 1]
% ----------------------------------------------------------------------

% Compute area of voronoi cell for a point on the Oz axis, i.e. a point
% in the 6-neighboorhoud of the center.
centre1 = pt112;
% neighbours of chosen point, sorted by angle
%voisins1 = [pt200;pt201;pt202;pt212;pt222;pt221;pt220;pt210];
%voisins1 = [pt212;pt222;pt122;pt022;pt012;pt002;pt102;pt202];
voisins1 = [pt121;pt011;pt101;pt211];

n1 = size(voisins1, 1);

% compute separating circles
planes1 = zeros(n1, 9);
for i=1:n1
    planes1(i,1:9) = normalizePlane(medianPlane(centre1, voisins1(i,:)));
%    circles1(i,1:7) = intersectPlaneSphere(planes1(i,:), sphere);
end

% compute circle intersections
lines1 = zeros(n1, 6);
for i=1:n1
    lines1(i,1:6) = intersectPlanes(planes1(i,:), ...
        planes1(mod(i,n1)+1,:));
    points1(2*i-1:2*i,1:3) = intersectLineSphere(lines1(i,:), sphere);
end

% keep only points with z>0
ind = dot(points1, repmat(centre1, [2*n1 1]), 2)>0;
points1 = points1(ind,:);
n1 = size(points1, 1);

% compute spherical area of each triangle [center  pt[i+1]%4   pt[i] ]
angles1 = zeros(n1, 1);
for i=1:n1
    pt1 = points1(i, :);
    pt2 = points1(mod(i  , n1)+1, :);
    pt3 = points1(mod(i+1, n1)+1, :);

    angles1(i) = sphericalAngle(pt1, pt2, pt3);
    angles1(i) = min(angles1(i), 2*pi-angles1(i));
end

% compute area of spherical polygon
area3 = sum(angles1)-pi*(n1-2);

%% Results
% -------------------------------------------------------------------

% display some results
disp('results : ')
pattern = 'area of cell in direction %d: %12.10f sr, = %12.10f %%';
disp(sprintf(pattern, 1, area1, area1*100/4/pi));
disp(sprintf(pattern, 2, area2, area2*100/4/pi));
disp(sprintf(pattern, 3, area3, area3*100/4/pi));

% return computed areas, formatted as fraction of sphere surface
area = [area1 area2 area3]/4/pi;

%% Internal functions
% -------------------------------------------------------------------
% This functions are part of a more general geometric library, but are
% included here for avoiding dependencies

function theta = angle3Points(varargin)
%ANGLE3POINTS return oriented angle made by 3 points
%
%   ALPHA = ANGLE3POINTS(P1, P2, P3).
%   Pi are either [1*2] arrays, or [N*2] arrays, in this case ALPHA is a
%   [N*1] array. The angle computed is the directed angle between line
%   (P2P1) and line (P2P3).
%   Result is always given in radians, between 0 and 2*pi.
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 23/02/2004.
%

%   HISTORY :
%   25/09/2005 : enable single parameter

if length(varargin)==3
    p1 = varargin{1};
    p2 = varargin{2};
    p3 = varargin{3};
elseif length(varargin)==1
    var = varargin{1};
    p1 = var(1,:);
    p2 = var(2,:);
    p3 = var(3,:);
end

% angle line (P2 P1)
theta = lineAngle(createLine(p2, p1), createLine(p2, p3));

function line = createLine(varargin)
%CREATELINE create a line with various inputs.
%
%   Line is represented in a parametric form : [x0 y0 dx dy]
%   x = x0 + t*dx
%   y = y0 + t*dy;
%
%
%   l = CREATELINE(p1, p2) return the line going through the two given
%   points.
%
%   l = CREATELINE(x0, y0, dx, dy) the line going through point (x0, y0)
%   and with direction vector(dx, dy).
%
%   l = CREATELINE(param) where param is an array of 4 values, create the
%   line oing through the point (param(1) param(2)), and with direction
%   vector (param(3) param(4)).
%
%   l = CREATELINE(theta) create a line originated at (0,0) and
%   with angle theta.
%
%   l = CREATELINE(rho, theta) create a line with normal theta, and with
%   min distance to origin equal to rho. rho can be negative, in this case,
%   the line is the same as with CREATELINE(-rho, theta+pi), but the
%   orientation is different.
%
%
%   Note : in all cases, parameters can be vertical arrays of the same
%   dimension. The result is then an array of lines, of dimensions [N*4].
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 31/10/2003.
%

if length(varargin)==1
    % Only one input parameter. It can be :
    % - line angle
    % - array of four parameters
    var = varargin{1};

    if size(var, 2)==4
        % 4 parameters of the line in a single array.
        line = var;
    elseif size(var, 2)==1
        % 1 parameter : angle of the line, going through origin.
        line = [zeros(size(var)) zeros(size(var)) cos(var) sin(var)];
    else
        error('wrong number of dimension for arg1 : can be 1 or 4');
    end

elseif length(varargin)==2
    % 2 input parameters. They can be :
    % - line angle and signed distance to origin.
    % - 2 points, then 2 arrays of 1*2 double.
    v1 = varargin{1};
    v2 = varargin{2};
    if size(v1, 2)==1
        % first param is angle of line, and second param is signed distance
        % to origin.
        line = [v1.*cos(v2) v1.*sin(v2) -sin(v2) cos(v2)];
    else
        % first input parameter is first point, and second input is the
        % second point.
        line = [v1(:,1), v1(:,2), v2(:,1)-v1(:,1), v2(:,2)-v1(:,2)];
    end

elseif length(varargin)==3
    % 3 input parameters :
    % first one is a point belonging to the line,
    % second and third ones are direction vector of the line (dx and dy).
    p = varargin{1};
    line = [p(:,1) p(:,2) varargin{2} varargin{3}];

elseif length(varargin)==4
    % 4 input parameters :
    % they are x0, y0 (point belongng to line) and dx, dy (direction vector
    % of the line).
    % All parameters should have the same size.
    line = [varargin{1} varargin{2} varargin{3} varargin{4}];
else
    error('Wrong number of arguments in ''createLine'' ');
end

function plane = createPlane(varargin)
%CREATEPLANE create a plane in parametrized form
%
%   Create a plane in the following format :
%   PLANE = [X0 Y0 Z0  DX1 DY1 DZ1  DX2 DY2 DZ2], where :
%   - (X0, Y0, Z0) is a point belonging to the plane
%   - (DX1, DY1, DZ1) is a first direction vector
%   - (DX2, DY2, DZ2) is a second direction vector
%
%
%
%   PLANE = createPlane(P1, P2, P3)
%   create a plane containing the 3 points
%
%   PLANE = createPlane(PTS)
%   The 3 points are packed into a single 3x3 array.
%
%   PLANE = createPlane(P0, N);
%   create a plane from a point and from a normal to the plane.
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 18/02/2005.
%

%   HISTORY :
%   24/11/2005 : add possibility to pack points for plane creation

if length(varargin)==1
    var = varargin{1};

    if iscell(var)
        plane = zeros(length(var), 9);
        for i=1:length(var)
            plane(i,:) = createPlane(var{i});
        end
    elseif size(var, 1)==3
        % 3 points in a single array
        p1 = var(1,:);
        p2 = var(2,:);
        p3 = var(3,:);

        % create direction vectors
        v1 = p2-p1;
        v2 = p3-p1;

        % create plane
        plane = [p1 v1 v2];
        return;
    end

elseif length(varargin)==2

    p0 = varargin{1};

    var = varargin{2};
    if size(var, 2)==2
        n = sph2cart2([var repmat(1, [size(var, 1) 1])]);
    elseif size(var, 2)==3
        n  = normalize3d(var);
    else
        error ('wrong number of parameters in createPlane');
    end

    % find a vector not colinear to the normal
    v0 = repmat([1 0 0], [size(p0, 1) 1]);
    if abs(cross(n, v0, 2))<1e-14
        v0 = repmat([0 1 0], [size(p0, 1) 1]);
    end

    % create direction vectors
    v1 = normalize3d(cross(n, v0, 2));
    v2 = -normalize3d(cross(v1, n, 2));

    plane = [p0 v1 v2];
    return;

elseif length(varargin)==3
    p1 = varargin{1};
    p2 = varargin{2};
    p3 = varargin{3};

    % create direction vectors
    v1 = p2-p1;
    v2 = p3-p1;

    plane = [p1 v1 v2];
    return;

else
    error('wrong number of arguments in "createPlane".');
end

function point = intersectLineSphere(line, sphere)
%INTERSECTLINESPHERE return intersection between a line and a sphere
%
%   GC = intersectLineSphere(LINE, SPHERE) return the two points which are
%   the intersection of the given line and sphere.
%   LINE   : [x0 y0 z0  dx dy dz]
%   SPHERE : [xc yc zc  R]
%   GC     : [x1 y1 z1 ; x2 y2 z2]
%
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 18/02/2005.
%

%   HISTORY

% difference between centers
dc = line(1:3)-sphere(1:3);

a = sum(line(:, 4:6).*line(:, 4:6), 2);
b = 2*sum(dc.*line(4:6), 2);
c = sum(dc.*dc, 2) - sphere(:,4).*sphere(:,4);

delta = b.*b -4*a.*c;

if delta>1e-14
    % find two roots of second order equation
    u1 = (-b -sqrt(delta))/2/a;
    u2 = (-b +sqrt(delta))/2/a;

    % convert into 3D coordinate
    point = [line(1:3)+u1*line(4:6) ; line(1:3)+u2*line(4:6)];

elseif abs(delta) > 1e-14
    % find unique root, and convert to 3D coord.
    u = -b/2./a;
    point = line(1:3) + u*line(4:6);

else
    point = zeros(0, 3);
    return;
end

function point = intersectPlaneLine(plane, line)
%INTERSECTPLANELINE return intersection between a plane and a line
%
%   PT = intersectPlaneSphere(PLANE, LINE) return the intersection point of
%   the given line and the given plane.
%   PLANE : [x0 y0 z0 dx1 dy1 dz1 dx2 dy2 dz2]
%   LINE :  [x0 y0 z0 dx dy dz]
%   PT :    [XI YI ZI]
%
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 17/02/2005.
%

%   HISTORY
%   24/11/2005 add support for multiple input

% unify sizes of data
if size(plane, 1)~=size(line, 1)
    if size(plane, 1)==1
        plane = repmat(plane, [size(line, 1) 1]);
    elseif size(line,1)==1
        line = repmat(line, [size(plane, 1) 1]);
    else
        error('line and plane do not have the same dimension');
    end
end

% plane normal
n = cross(plane(:,4:6), plane(:, 7:9), 2);

% test if line and plane are parallel
if abs(dot(n, line(:,4:6), 2))<1e-14
    point = [NaN NaN NaN];
    return;
end

% difference between origins of plane and line
dp = plane(:,1:3) - line(:,1:3);

% relative position of intersection on line
t = dot(n, dp, 2)/dot(n, line(:,4:6), 2);

% compute coord of intersection point
point = line(:,1:3) + t*line(:,4:6);

function line = intersectPlanes(plane1, plane2)
%INTERSECTPLANES return intersection between 2 planes in space
%
%   PT = intersectPlanes(PLANE1, PLANE2) return the straight line belonging
%   to both planes
%   PLANE : [x0 y0 z0 dx1 dy1 dz1 dx2 dy2 dz2]
%   LINE :  [x0 y0 z0 dx dy dz]
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 17/02/2005.
%

%   HISTORY

% plane normal
n1 = normalize3d(cross(plane1(:,4:6), plane1(:, 7:9), 2));
n2 = normalize3d(cross(plane2(:,4:6), plane2(:, 7:9), 2));

% test if planes are parallel
if abs(cross(n1, n2, 2))<1e-14
    line = [NaN NaN NaN NaN NaN NaN];
    return;
end

% Uses hessian form, ie : N.p = d
% I this case, d can be found as : -N.p0, when N is normalized
d1 = dot(n1, plane1(:,1:3), 2);
d2 = dot(n2, plane2(:,1:3), 2);

% compute dot products
dot1 = dot(n1, n1, 2);
dot2 = dot(n2, n2, 2);
dot12 = dot(n1, n2, 2);

det = dot1*dot2 - dot12*dot12;
c1 = (d1*dot2 - d2*dot12)./det;
c2 = (d2*dot1 - d1*dot12)./det;

p0 = c1*n1 + c2*n2;
dp = cross(n1, n2, 2);

line = [p0 dp];

function theta = lineAngle(varargin)
%LINEANGLE return angle between lines
%
%   a = LINEANGLE(line) return the angle between horizontal, right-axis
%   and the given line. Angle is fiven in radians, between 0 and 2*pi,
%   in counter-clockwise direction.
%
%   a = LINEANGLE(line1, line2) return the directed angle between the
%   two lines. Angle is given in radians between 0 and 2*pi.
%
%   see createLine for more details on line representation.
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 31/10/2003.
%

%   HISTORY :
%   19/02/2004 : added support for multiple lines.

nargs = length(varargin);
if nargs == 1
    % one line
    line = varargin{1};
    theta = mod(atan2(line(:,4), line(:,3)) + 2*pi, 2*pi);
elseif nargs==2
    % two lines
    theta1 = lineAngle(varargin{1});
    theta2 = lineAngle(varargin{2});
    theta = mod(theta2-theta1+2*pi, 2*pi);
end

function plane = medianPlane(p1, p2)
%MEDIANPLANE create a plane in the middle of 2 points
%
%   plane = medianPlane(P1, P2)
%   plane is perpendicular to line (P1 P2) and contains the midpoint of p1
%   and p2.
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 18/02/2005.
%

p0 = (p1 + p2)/2;
n = p2-p1;

plane = createPlane(p0, n);

function vn = normalize(v)
%NORMALIZE normalize a vector
%
%   V2 = normalize(V);
%   return the normalization of vector V, such that ||V|| = 1. V can be
%   either a row or a column vector.
%
%   When V is a MxN array, normalization is performed for each row of the
%   array.
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 29/11/2004.
%

dim = size(v);
if dim(1)==1 || dim(2)==1
    vn = v/sqrt(sum(v.*v));
else
    vn = v./repmat(sqrt(sum(v.*v, 2)), [1 dim(2)]);
end

function vn = normalize3d(v)
%NORMALIZE3D normalize a 3D vector
%
%   V2 = normalize3d(V);
%   return the normalization of vector V, such that ||V|| = 1. Vector V is
%   given in vertical form.
%
%   When V is a Nx3 array, normalization is performed for each row of the
%   array.
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 29/11/2004.
%

%   HISTORY
%   30/11/2005  : correct a bug

n = sqrt(v(:,1).*v(:,1) + v(:,2).*v(:,2) + v(:,3).*v(:,3));
vn = v./[n n n];

function plane2 = normalizePlane(plane1)
%NORMALIZEPLANE normalize parametric form of a plane
%
%   plane2 = normalizePlane(plane1);
%   transform the plane PANE1 in the following format :
%   [X0 Y0 Z0  DX1 DY1 DZ1  DX2 DY2 DZ2], where :
%   - (X0, Y0, Z0) is a point belonging to the plane
%   - (DX1, DY1, DZ1) is a first direction vector
%   - (DX2, DY2, DZ2) is a second direction vector
%   into another plane, with the same format, but with :
%   - (x0 y0 z0) is the closest point of plane to origin
%   - (DX1 DY1 DZ1) has norm equal to 1
%   - (DX2 DY2 DZ2) has norm equal to 1 and is orthogonal to (DX1 DY1 DZ1)
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 21/02/2005.
%

%   HISTORY :

% compute origin point of the plane
p0 = projPointOnPlane([0 0 0], plane1);

% compute first direction vector
d1 = normalize3d(plane1(:,4:6));

% compute second direction vector
n = normalize3d(planeNormal(plane1));
d2 = -normalize3d(cross(d1, n));

% create the resulting plane
plane2 = [p0 d1 d2];

function n = planeNormal(plane)
%PLANENORMAL compute the normal to a plane
%
%   N = planeNormal(PLANE)
%   compute the normal of the given plane
%   PLANE : [x0 y0 z0 dx1 dy1 dz1 dx2 dy2 dz2]
%   N : [dx dy dz]
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 17/02/2005.
%

%   HISTORY

% plane normal
n = cross(plane(:,4:6), plane(:, 7:9), 2);

function pos = planePosition(point, plane)
%PLANEPOSITION compute position of a point on a plane
%
%   PT2 = PLANEPOSITION(POINT, PLANE)
%   POINT has format [X Y Z], and plane has format
%   [X0 Y0 Z0  DX1 DY1 DZ1  DX2 DY2 DZ2], where :
%   - (X0, Y0, Z0) is a point belonging to the plane
%   - (DX1, DY1, DZ1) is a first direction vector
%   - (DX2, DY2, DZ2) is a second direction vector
%
%   Result PT2 has the form [XP YP], with [XP YP] coordinate of the point
%   in the coordinate system of the plane.
%
%
%   CAUTION :
%   WORKS ONLY FOR PLANES WITH ORTHOGONAL DIRECTION VECTORS
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 21/02/2005.
%

%   HISTORY :
%   24/11/2005 add support for multiple input

% unify size of data
if size(point, 1)~=size(plane, 1)
    if size(point, 1)==1
        point = repmat(point, [size(plane, 1) 1]);
    elseif size(plane, 1)==1
        plane = repmat(plane, [size(point, 1) 1]);
    else
        error('point and plane do not have the same dimension');
    end
end

p0 = plane(:,1:3);
d1 = plane(:,4:6);
d2 = plane(:,7:9);

s = dot(point-p0, d1, 2)./vecnorm3d(d1);
t = dot(point-p0, d2, 2)./vecnorm3d(d2);

pos = [s t];

function point = projPointOnPlane(point, plane)
%PROJPOINTONPLANE return the projection of a point on a plane
%
%   PT2 = PROJECTEDPOINT(PT1, PLANE).
%   Compute the (orthogonal) projection of point PT1 onto the line PLANE.
%
%   Function works also for multiple points and planes. In this case, it
%   returns multiple points.
%   Point PT1 is a [N*3] array, and PLANE is a [N*9] array (see createPlane
%   for details). Result PT2 is a [N*3] array, containing coordinates of
%   orthogonal projections of PT1 onto planes PLANE.
%
%   See also planePosition
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 18/02/2005.
%

n = planeNormal(plane);
line = [point repmat(n, [size(point, 1) 1])];
point = intersectPlaneLine(plane, line);

function varargout = sph2cart2(varargin)
%SPH2CART2 convert spherical coordinate to cartesian coordinate
%
%   usage :
%   C = SPH2CART2(S)
%   C = SPH2CART2(PHI, THETA)       (assume rho = 1)
%   C = SPH2CART2(PHI, THETA, RHO)
%   [X, Y, Z] = SPH2CART2(PHI, THETA, RHO);
%
%   S = [phi theta rho] (sphercial coordiante).
%   C = [X Y Z]  (cartesian coordinate)
%
%   Math convention is used : theta is angle with vertical, 0 for north
%   pole, +pi for south pole, pi/2 for points with z=0.
%   phi is the same as matlab cart2sph : angle from Ox axis, counted
%   counter-clockwise.
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 18/02/2005.
%

%   HISTORY
%   22/03/2005 : make test for 2 args, and add radius if not specified for
%       1 arg.

if length(varargin)==1
    var = varargin{1};
    if size(var, 2)==2
        var = [var ones(size(var, 1), 1)];
    end
elseif length(varargin)==2
    var = [varargin{1} varargin{2} ones(size(varargin{1}))];
elseif length(varargin)==3
    var = [varargin{1} varargin{2} varargin{3}];
end

[x y z] = sph2cart(var(:,1), pi/2-var(:,2), var(:,3));

if nargout == 1 || nargout == 0
    varargout{1} = [x, y, z];
else
    varargout{1} = x;
    varargout{2} = y;
    varargout{3} = z;
end

function alpha = sphericalAngle(p1, p2, p3)
%SPHERICALANGLE compute angle on the sphere
%
%   ALPHA = sphericalAngle(P1, P2, P3)
%   compute angle (P1, P2, P2), in radians, between 0 and 2*PI.
%
%   Points are given either as [x y z] (there will be normalized to lie on
%   the unit sphere), or as [phi theta], with phi being the longitude in [0
%   2*PI] and theta being the elevation on horizontal [-pi/2 pi/2].
%
%
%   NOTE :
%   this is an 'oriented' version of the angle computation, that is, the
%   result of sphericalAngle(P1, P2, P3) equals
%   2*pi-sphericalAngle(P3,P2,P1). To have the more classical relation
%   (with results given betwen 0 and PI), it suffices to take the minimum
%   of angle and 2*pi-angle.
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 21/02/2005.
%

%   HISTORY

% test if points are given as matlab spherical coordinate
if size(p1, 2) ==2
    [x y z] = sph2cart(p1(:,1), p1(:,2));
    p1 = [x y z];
    [x y z] = sph2cart(p2(:,1), p2(:,2));
    p2 = [x y z];
    [x y z] = sph2cart(p3(:,1), p3(:,2));
    p3 = [x y z];
end

% normalize points
p1 = normalize3d(p1);
p2 = normalize3d(p2);
p3 = normalize3d(p3);

% create the plane tangent to the unit sphere and containing central point
plane = createPlane(p2, p2);

% project the two other points on the plane
pi1 = planePosition(intersectPlaneLine(plane, [0 0 0 p1]), plane);
pi3 = planePosition(intersectPlaneLine(plane, [0 0 0 p3]), plane);

% compute angle on the tangent plane
alpha = angle3Points(pi1, [0 0], pi3);

function n = vecnorm3d(v)
%VECNORM3D compute euclidean norm of vector or of set of 3D vectors
%
%   n = vecnorm(V);
%   return euclidean norm of vector V.
%
%   When V is a Nx3 array, compute norm for each vector of the array.
%   Vector are given as rows. Result is then a [N*1] array.
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 21/02/2005.
%

%   HISTORY

n = sqrt(sum(v.*v, 2));


%PROBLEM SPECIFICATION
% Specification of parameters, grid and boundary conditions

global Re

tic
if geval == 1	% Horizontal Poiseuille flow to the right
  dt = 0.1;	% Time step
  tend = 10;	% End time
  Re = 100;	% Reynolds number based on unit length and unit velocity
  central = 1;	% Enter 1 for central scheme or something else for upwind scheme
  omega = 1/2;  % Omega-scheme for time-stepping
  umax = 0.75;	% Estimates of max. velocity components required for stability
  vmax = 0;	% condition for Adams-Bashforth-crank-Nicolson scheme

  xseglen = [1,1];	% Enter segment lengths of horizontal boundary in
		% order of increasing x. Number of segments is arbitrary.
		% These segments are used for grid generation and boundary
		% conditions.
  yseglen = [1,1];	% Similar to xseglen in y-direction.
  nx = [4,4];	% Number of cells along x-segments.
  ny = [4,4];	% Number of cells along y-segments.

  % Type of boundary condition: 1: no-slip
  %				2: inflow
  %				3: outflow
  % Give type of boundary condition along lower (first row) and upper
  %(second row) horizontal segments in order of increasing x:

  xbc = [1,1; 1,1];

  % Give type of boundary condition along left (first row) and right
  % (second row) vertical segments in order of increasing y:

  ybc = [2,2; 3,3];

elseif geval == 2	% Vertical Poiseuille flow upward
  dt = 0.5;	% Time step
  tend = 10;	% End time
  Re = 100;	% Reynolds number based on unit length and unit velocity
  central = 1;	% Enter 1 for central scheme or something else for upwind scheme
  omega = 1/2;  % Omega-scheme for time-stepping
  umax = 0;	% Estimates of max. velocity components required for stability
  vmax = 0.75;	% condition for Adams-Bashforth-crank-Nicolson scheme

  xseglen = [1,1];	% Enter segment lengths of horizontal boundary in
		% order of increasing x. Number of segments is arbitrary.
		% These segments are used for grid generation and boundary
		% conditions.
  yseglen = [1,1];	% Similar to xseglen in y-direction.
  nx = [8,16];	% Number of cells along x-segments.
  ny = [8,8];	% Number of cells along y-segments.

  % Type of boundary condition: 1: no-slip
  %				2: inflow
  %				3: outflow
  % Give type of boundary condition along lower (first row) and upper
  %(second row) horizontal segments in order of increasing x:

  xbc = [2,2; 3,3];

  % Give type of boundary condition along left (first row) and right
  % (second row) vertical segments in order of increasing y:

  ybc = [1,1; 1,1];

elseif geval == 3	% Backward facing step
  dt = 3;	% Time step
  tend = 140;	% End time
  Re = 100;	% Reynolds number based on unit length and unit velocity
  central = 1;	% Enter 1 for central scheme or something else for upwind scheme
  omega = 1/2;  % Omega-scheme for time-stepping
  umax = 1.25;	% Estimates of max. velocity components required for stability
  vmax = 0.25;	% condition for Adams-Bashforth-Crank-Nicolson scheme
  		% (not used in program ns1)

  xseglen = [5,5];	% Enter segment lengths of horizontal boundary in
		% order of increasing x. Number of segments is arbitrary.
		% These segments are used for grid generation and boundary
		% conditions.
  yseglen = [1,1];	% Similar to xseglen in y-direction.
  nx = [20,10];	% Number of cells along x-segments.
  ny = [25,25];	% Number of cells along y-segments.

  % Type of boundary condition: 1: no-slip
  %				2: inflow
  %				3: outflow
  % Give type of boundary condition along lower (first row) and upper
  %(second row) horizontal segments in order of increasing x:

  xbc = [1,1; 1,1];

  % Give type of boundary condition along left (first row) and right
  % (second row) vertical segments in order of increasing y:

  ybc = [1,2; 3,3];

elseif geval == 4	% Driven cavity
  dt = 0.5;	% Time step
  tend = 25;	% End time
  Re = 200;	% Reynolds number based on unit length and unit velocity
  central = 1;	% Enter 1 for central scheme or something else for upwind scheme
  omega = 0.55;  % Omega-scheme for time-stepping
  umax = 1;	% Estimates of max. velocity components required for stability
  vmax = 0;	% condition for Adams-Bashforth-crank-Nicolson scheme

  xseglen = [1,1];	% Enter segment lengths of horizontal boundary in
		% order of increasing x. Number of segments is arbitrary.
		% These segments are used for grid generation and boundary
		% conditions.
  yseglen = [1,1];	% Similar to xseglen in y-direction.
  nx = [15,15];	% Number of cells along x-segments.
  ny = [15,15];	% Number of cells along y-segments.
  % Type of boundary condition: 1: no-slip
  %				2: inflow
  %				3: outflow
  % Give type of boundary condition along lower (first row) and upper
  %(second row) horizontal segments in order of increasing x:

  xbc = [1,1; 2,2];

  % Give type of boundary condition along left (first row) and right
  % (second row) vertical segments in order of increasing y:

  ybc = [1,1; 1,1];

elseif geval == 5	% Uniform flow under angle alpha in [0,pi/2] without
			% segmentation
  alpha = pi/4;
  dt = 3;	% Time step
  tend = 60;	% End time
  Re = 100;	% Reynolds number based on unit length and unit velocity
  central = 1;	% Enter 1 for central scheme or something else for upwind scheme
  omega = 1/2;  % Omega-scheme for time-stepping
  umax = abs(cos(alpha));% Estimates of max. velocity components required for stability
  vmax = abs(sin(alpha));% condition for Adams-Bashforth-crank-Nicolson scheme

  xseglen = [1];	% Enter segment lengths of horizontal boundary in
		% order of increasing x. Number of segments is arbitrary.
		% These segments are used for grid generation and boundary
		% conditions.
  yseglen = [1];	% Similar to xseglen in y-direction.
  nx = [2];	% Number of cells along x-segments.
  ny = [2];	% Number of cells along y-segments.

  % Type of boundary condition: 1: no-slip
  %				2: inflow
  %				3: outflow
  % Give type of boundary condition along lower (first row) and upper
  %(second row) horizontal segments in order of increasing x:

  xbc = [2; 3];

  % Give type of boundary condition along left (first row) and right
  % (second row) vertical segments in order of increasing y:

  ybc = [2; 3];

elseif geval == 6	% Uniform flow under angle alpha in [0,pi/2] with
			% segmentation
  alpha = pi/4;
  dt = 3;	% Time step
  tend = 60;	% End time
  Re = 100;	% Reynolds number based on unit length and unit velocity
  central = 1;	% Enter 1 for central scheme or something else for upwind scheme
  omega = 1/2;  % Omega-scheme for time-stepping
  umax = abs(cos(alpha));% Estimates of max. velocity components required for stability
  vmax = abs(sin(alpha));% condition for Adams-Bashforth-crank-Nicolson scheme

  xseglen = [1,1];	% Enter segment lengths of horizontal boundary in
		% order of increasing x. Number of segments is arbitrary.
		% These segments are used for grid generation and boundary
		% conditions.
  yseglen = [1,1];	% Similar to xseglen in y-direction.
  nx = [2,6];	% Number of cells along x-segments.
  ny = [2,3];	% Number of cells along y-segments.

  % Type of boundary condition: 1: no-slip
  %				2: inflow
  %				3: outflow
  % Give type of boundary condition along lower (first row) and upper
  %(second row) horizontal segments in order of increasing x:

  xbc = [2,2; 3,3];

  % Give type of boundary condition along left (first row) and right
  % (second row) vertical segments in order of increasing y:

  ybc = [2,2; 3,3];

elseif geval == 7	% Horizontal Poiseuille flow to the left
  dt = 0.5;	% Time step
  tend = 60;	% End time
  Re = 100;	% Reynolds number based on unit length and unit velocity
  central = 1;	% Enter 1 for central scheme or something else for upwind scheme
  omega = 1/2;  % Omega-scheme for time-stepping
  umax = 0.75;	% Estimates of max. velocity components required for stability
  vmax = 0;	% condition for Adams-Bashforth-crank-Nicolson scheme

  xseglen = [1,1];	% Enter segment lengths of horizontal boundary in
		% order of increasing x. Number of segments is arbitrary.
		% These segments are used for grid generation and boundary
		% conditions.
  yseglen = [1,1];	% Similar to xseglen in y-direction.
  nx = [4,4];	% Number of cells along x-segments.
  ny = [4,4];	% Number of cells along y-segments.

  % Type of boundary condition: 1: no-slip
  %				2: inflow
  %				3: outflow
  % Give type of boundary condition along lower (first row) and upper
  %(second row) horizontal segments in order of increasing x:

  xbc = [1,1; 1,1];

  % Give type of boundary condition along left (first row) and right
  % (second row) vertical segments in order of increasing y:

  ybc = [3,3; 2,2];

else
  error('Wrong value in input for parameter geval')
end

if (size(xseglen)~=size(nx))|(size(yseglen)~=size(ny))
  error('Wrong correspondence between xseglen,nx or yseglen,ny in problem_specification')
end
if (size(xbc(1,:))~=size(nx))|(size(ybc(1,:))~=size(nx))
  error('Wrong correspondence between nx,xbc or ny,ybc in problem_specification')
end

tijd = toc; disp(['problem_specification time = ',num2str(tijd)])


function[newPop] = normGeomSelect(oldPop,options)
% NormGeomSelect is a ranking selection function based on the normalized
% geometric distribution.
%
% function[newPop] = normGeomSelect(oldPop,options)
% newPop  - the new population selected from the oldPop
% oldPop  - the current population
% options - options to normGeomSelect [gen probability_of_selecting_best]

% Binary and Real-Valued Simulation Evolution for Matlab
% Copyright (C) 1996 C.R. Houck, J.A. Joines, M.G. Kay
%
% C.R. Houck, J.Joines, and M.Kay. A genetic algorithm for function
% optimization: A Matlab implementation. ACM Transactions on Mathmatical
% Software, Submitted 1996.
%
% 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 1, 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. A copy of the GNU
% General Public License can be obtained from the
% Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

q=options(2); 				% Probability of selecting the best
e = size(oldPop,2); 			% Length of xZome, i.e. numvars+fit
n = size(oldPop,1); 			% Number of individuals in pop
newPop = zeros(n,e); 			% Allocate space for return pop
fit = zeros(n,1); 			% Allocates space for prob of select
x=zeros(n,2); 			        % Sorted list of rank and id
x(:,1) =[n:-1:1]'; 			% To know what element it was
[y x(:,2)] = sort(oldPop(:,e)); 	% Get the index after a sort
r = q/(1-(1-q)^n); 			% Normalize the distribution, q prime
fit(x(:,2))=r*(1-q).^(x(:,1)-1); 	% Generates Prob of selection
fit = cumsum(fit); 			% Calculate the cumulative prob. func
rNums=sort(rand(n,1)); 			% Generate n sorted random numbers
fitIn=1; newIn=1; 			% Initialize loop control
while newIn<=n 				% Get n new individuals
  if(rNums(newIn)<fit(fitIn))
    newPop(newIn,:) = oldPop(fitIn,:); 	% Select the fitIn individual
    newIn = newIn+1; 			% Looking for next new individual
  else
    fitIn = fitIn + 1; 			% Looking at next potential selection
  end
end


function s = resampdet(p,m,n);
%RESAMPDET Deterministic resampling
%
%   Description
%   S = RESAMPDET(P) returns a new set of indices according to the
%   probabilities P. P is array of probabilities, which are not
%   necessarily normalized, though they must be non-negative, and
%   not all zero. The size of S is the size of P.
%
%   S = RESAMPDET(P,M,N) returns M by N matrix.
%
%   S = RESAMPDET(P,M) returns M by M matrix.
%
%   Default is to use no-sort resampling. For sorted resampling use
%    [PS,PI]=SORT(P);
%    S=PI(RESAMPDET(PS));
%   Sorted re-sampling is slower but has smaller variance. Note
%   that deterministic resampling is not unbiased. Stratified
%   resampling (RESAMPSTR) is unbiased, almost as fast as
%   deterministic resampling, and has only slightly larger
%   variance.
%
%   In deterministic resampling indices are sampled using
%   deterministic numbers u_j~(j-a)/n, for fixed a in [0,1) and
%   n is length of P. Compare this to simple random resampling
%   where u_j~U[0,n]. See, Kitagawa, G., Monte Carlo Filter and
%   Smoother for Non-Gaussian Nonlinear State Space Models,
%   Journal of Computational and Graphical Statistics, 5(1):1-25,
%   1996.
%
%   See also RESAMPSIM, RESAMPRES, RESAMPSTR
%
% Copyright (c) 2003-2004 Aki Vehtari

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

if nargin<2
    [m,n]=size(p);
elseif nargin==2
    n=m;
end
mn=m.*n;
pn=p./sum(p(:)).*mn;
fpn=floor(pn);
s=zeros(m,n);
k=0;
c=0.5;
for i=1:numel(p)
  if pn(i)>=1
    a=fpn(i);
    pn(i)=pn(i)-a;
    s(k+[1:a])=i;
    k=k+a;
  end
  c=c+pn(i);
  if c>=1
    k=k+1;
    s(k)=i;
    c=c-1;
  end
end


function f = prolong(f, nOut)
%PROLONG   Manually adjust the number of points used in a TRIGTECH.
%   G = PROLONG(F, N) returns a TRIGTECH G where LENGTH(G) = N and G represents
%   the same function as F but using more or less coefficients than F.
%
%   If N < LENGTH(F) the representation is compressed by chopping
%   coefficients, which may result in a loss of accuracy.
%
%   If N > LENGTH(F) the coefficients are padded with zeros.
%
% See also ALIAS.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Get the number of coefficients.
n = length(f);

% Return if nOut == n
if ( nOut == n )
    return
end

% Get coefficients
coeffs = f.coeffs;

% If n is even extend to coeffs to n+1
if ( mod(n,2) == 0 )
    coeffs = [.5*coeffs(1,:);coeffs(2:end,:);.5*coeffs(1,:)];
    n = n + 1;
end

% Return if nOut == n
if ( nOut == n )
    f.coeffs = coeffs;
    f.values = f.coeffs2vals(f.coeffs);
    f.values(:,f.isReal) = real(f.values(:,f.isReal));
    return
end

% Pad with zeros when nOut > n:
if ( nOut > n )
    kup = ceil((nOut-n)/2);
    kdown = floor((nOut-n)/2);
    coeffs = [zeros(kup, size(coeffs, 2)); coeffs; zeros(kdown, size(coeffs,2))];
    f.coeffs = coeffs;
    f.values = f.coeffs2vals(f.coeffs);
    f.values(:,f.isReal) = real(f.values(:,f.isReal));
    return
end

% Chop coefficients when nOut < n:
if ( nOut < n )
    kup = floor((n-nOut)/2);
    kdown = ceil((n-nOut)/2);
    coeffs(end-kdown+1:end,:) = [];
    coeffs(1:kup,:) = [];
    if ( kup < kdown )
        coeffs(1,:) = 2*coeffs(1,:); % scale coefficients in even case
    end
    f.coeffs = coeffs;
    f.values = f.coeffs2vals(f.coeffs);
    f.values(:,f.isReal) = real(f.values(:,f.isReal));
    return
end

end


function e = rmModelSearchFit_twoGaussiansDoGSigmasOnly(p,Y,XvYv,stim,t)
% rmModelSearchFit_twoGaussians - actual fit function of rmSearchFit
%
% error = rmModelSearchFit_twoGaussians(p,Y,trends,Gx,Gy,stim,rawrss);
%
% Basic barebones fit of a single time-series. Error is returned in
% percentage: 100% is RSS of unfitted time-series. This way we can quantify
% the improvement of the fit independend of the variation in the raw
% time-series.
%
% 2006/06 SOD: wrote it.
% 2006/12 SOD: modifications for fmincon, this is litterally called >>10000
% times so we cut every corner possible.

% make RF (taken from rfGaussian2d)
p = -2.*(p.^2);
RF = zeros(numel(XvYv),2);
RF(:,1) = exp( (XvYv) ./ p(1) );
RF(:,2) = exp( (XvYv) ./ p(2) );

% make prediction (taken from rfMakePrediction)
X = [stim * RF t];

% fit
%b = pinv(X)*Y;
[U,S,V] = svd(X,0);
s = diag(S);
tol = numel(X) * eps(max(s));
r = sum(s > tol);
if (r == 0)
    pinvX = zeros(size(X'));
else
    s = diag(ones(r,1)./s(1:r));
    pinvX = V(:,1:r)*s*U(:,1:r)';
end
b = pinvX*Y;

% force positive fit
b(1) = abs(b(1));

%force negative b2 fit
b(2) = -(abs(b(2)));

% The center of the pRF should be positive. Thus, b(1)+b(2)>=0, or
% b(2) should be larger than -b(1) (implemented).
b(2) = max(b(2),-b(1));

% force second Gaussian to be negative
b(2) = -abs(b(2));

% compute residual sum of squares (e)
e = norm(Y - X*b);

return;


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Jellyfish Example Courtesy of Alexander P. Hoover, PhD
%
% Converted from IBAMR: 1/16/2018 by NAB.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function Make_Jelly_Geometry()

close all;
clear all;
L = 8;                              % height of computational domain (m) for keeping desired resolution
Lh = 10;                            % actual height of computational domain (m) (MATCHES INPUT2D)
Lw = 3;                             % width of computational domain (m) (MATCHES INPUT2D)
N = 96;                            % number of Cartesian grid meshwidths at the finest level of the AMR grid
dx = L/N;                           % Cartesian mesh width (m)
ds = dx/2;

a=.5;                               % bell radius (semi-minor axis, horizontal axis, note width=2a)
b=.75;                              % bell semi-major axis
d=-0.25;
factor_a=.8;

F=1e5; %5e0

theta=zeros(1000,1);
theta_lim=asin(d/b);
theta_test=pi/2;

x_points=zeros(1000,1);
z_points=zeros(1000,1);
id_points=zeros(1000,1);
offset = 0;

kappa_spring = 1e7; %1e5               % spring constant (Newton)
kappa_beam = 2.5e5; %1e5    %5e3              % beam stiffness constant (Newton m^2)
%kappa_beam_flexible = kappa_beam/5;   % beam stiffness constant (Newton m^2)
kappa_target = kappa_spring;           % target point penalty spring constant (Newton)

c=0;
while(theta_test<(pi-theta_lim))
    c=c+1;
    theta(c)=theta_test;

    x_points(c)=a*cos(theta(c));
    z_points(c)=b*sin(theta(c));
    id_points(c)=c-1;

    theta_test=ds/((a*sin(theta(c)))^(2)+(b*cos(theta(c)))^(2))^(.5)+theta(c);

end

c_stiff=c;

npts=2*c-1;
npts_wing=floor(npts/2);
npts_musc=floor(npts_wing/4);

for j=(c+1):(npts)
    x_points(j)=-1*x_points(j-c+1);
    z_points(j)=z_points(j-c+1);
    id_points(j)=j-1;
end

mesh_name = 'jelly';
xShift = 1.5;
yShift = 2;

x_points=x_points(1:npts)+xShift;
z_points=z_points(1:npts)+yShift;
it_points=id_points(1:npts);

plot(x_points(:),z_points(:),'*'); hold on;
axis([0 8 0 8])

% Lag Pts to Mess up Flow At Edge
xBlock = ds:4*ds:Lw-ds;
yBlock = (Lh-5*ds)*ones(1,length(xBlock))+ds;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Print .vertex information
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

vertex_fid = fopen([mesh_name num2str(N) '.vertex'], 'w');

    fprintf(vertex_fid, '%d\n', npts + npts_musc*2 + length(xBlock));
    lag_ct = 0;

    %
    % bell
    %
    for j=1:npts
        fprintf(vertex_fid, '%1.16e %1.16e\n', x_points(j), z_points(j));
        lag_ct = lag_ct + 1;
    end

    %
    % muscles
    %
    for s = 1:npts_musc
        fprintf(vertex_fid, '%1.16e %1.16e\n', x_points(npts_wing+1-npts_musc+s), z_points(npts_wing+1-npts_musc+s));
        plot(x_points(npts_wing+1-npts_musc+s),z_points(npts_wing+1-npts_musc+s),'r*'); hold on;
        lag_ct = lag_ct + 1;
    end
    for s = 1:npts_musc
        fprintf(vertex_fid, '%1.16e %1.16e\n', x_points(npts-npts_musc+s), z_points(npts-npts_musc+s));
        plot(x_points(npts-npts_musc+s),z_points(npts-npts_musc+s),'r*'); hold on;
        lag_ct = lag_ct + 1;
    end

    for ii=1:length(xBlock)
        fprintf(vertex_fid, '%1.16e %1.16e\n', xBlock(ii), yBlock(ii));
    end

fclose(vertex_fid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Print .spring information
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

spring_fid = fopen([mesh_name num2str(N) '.spring'], 'w');

    npts_spring_type1=npts-1;

    fprintf(spring_fid, '%d\n', npts-1 + npts_musc);

    fprintf('\nNumber of springs before muscles: %d \n\n',npts-1)

    factor = 1;%ds^2/ds;

    %
    % bell
    %
    for s = 1:c-1
        resting=sqrt((x_points(s)-x_points(s+1))^(2)+(z_points(s)-z_points(s+1))^(2));
        fprintf(spring_fid, '%d %d %1.16e %1.16e %d\n', id_points(s)+1, id_points(s+1)+1, kappa_spring*ds/(ds^2)*factor, resting, 1);
    end
    for s = c+1:npts-1
        resting=sqrt((x_points(s)-x_points(s+1))^(2)+(z_points(s)-z_points(s+1))^(2));
        fprintf(spring_fid, '%d %d %1.16e %1.16e %d\n', id_points(s)+1, id_points(s+1)+1, kappa_spring*ds/(ds^2)*factor, resting, 1);
    end
    resting=sqrt((x_points(1)-x_points(c+1))^(2)+(z_points(1)-z_points(c+1))^(2));
    fprintf(spring_fid, '%d %d %1.16e %1.16e %d\n', id_points(1)+1, id_points(c+1)+1, kappa_spring*ds/(ds^2)*factor, resting, 1);

    %
    % muscles
    %
    for s = 1:npts_musc
        fprintf(spring_fid, '%d %d %1.16e %1.16e %d\n',npts+s-1+1, npts+s+npts_musc-1+1, F, 0, 1);
    end

    fclose(spring_fid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Print .nonInv_beam information
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

beam_fid = fopen([mesh_name num2str(N) '.nonInv_beam'], 'w');

    fprintf(beam_fid, '%d\n', npts-2);

    factor=1;% = (ds^4)/ds;

    for s = 2:c-1
        C1 = x_points(s-1)+x_points(s+1)-2*x_points(s);
        C2 = z_points(s-1)+z_points(s+1)-2*z_points(s);
        fprintf(beam_fid, '%d %d %d %1.16e %1.16e %1.16e\n', id_points(s-1)+1, id_points(s)+1, id_points(s+1)+1, kappa_beam*ds/(ds^4)*factor, C1, C2);
    end
    for s = c+2:npts-1
        C1 = x_points(s-1)+x_points(s+1)-2*x_points(s);
        C2 = z_points(s-1)+z_points(s+1)-2*z_points(s);
        fprintf(beam_fid, '%d %d %d %1.16e %1.16e %1.16e\n', id_points(s-1)+1, id_points(s)+1, id_points(s+1)+1, kappa_beam*ds/(ds^4)*factor, C1, C2);
    end

    C1 = x_points(c+2)+x_points(1)-2*x_points(c+1);
    C2 = z_points(c+2)+z_points(1)-2*z_points(c+1);
    fprintf(beam_fid, '%d %d %d %1.16e %1.16e %1.16e\n', id_points(c+2)+1, id_points(c+1)+1, id_points(1)+1, kappa_beam*ds/(ds^4)*factor, C1, C2);

    C1 = x_points(c+1)+x_points(2)-2*x_points(1);
    C2 = z_points(c+1)+z_points(2)-2*z_points(1);
    fprintf(beam_fid, '%d %d %d %1.16e %1.16e %1.16e\n', id_points(c+1)+1, id_points(1)+1, id_points(2)+1, kappa_beam*ds/(ds^4)*factor, C1, C2);

    fclose(beam_fid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% PRINT TARGET POINTS!!!
%
% print target points (flow blocker along edge)
k_Target = 2.5e6;
nBefore = lag_ct; % Counts pts in jellyfish for bookkeeping for .target file
struct_name = ['jelly' num2str(N)];
print_Lagrangian_Target_Pts(xBlock,k_Target,struct_name,nBefore)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION: prints TARGET points to a file called 'struct_name'.target
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function print_Lagrangian_Target_Pts(xLag,k_Target,struct_name,nBefore)

    N = length(xLag);
    Nstart = nBefore+1;
    Nend = nBefore+N;

    target_fid = fopen([struct_name '.target'], 'w');

    fprintf(target_fid, '%d\n', N );

    %Loops over all Lagrangian Pts.
    for s = Nstart:Nend
        fprintf(target_fid, '%d %1.16e\n', s, k_Target);
    end

    fclose(target_fid);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spherical Harmonic Modeling and Analysis Toolkit (SPHARM-MAT) is a 3D
% shape modeling and analysis toolkit.
% It is a software package developed at Shenlab in Center for Neuroimaging,
% Indiana University (SpharmMat@gmail.com, http://www.iupui.edu/~shenlab/)
% It is available to the scientific community as copyright freeware
% under the terms of the GNU General Public Licence.
%
% Copyright 2009, 2010, ShenLab, Center for Neuroimaging, Indiana University
%
% This file is part of SPHARM-MAT.
%
% SPHARM-MAT 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.
%
% SPHARM-MAT 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 SPHARM-MAT. If not, see <http://www.gnu.org/licenses/>.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%
% Rotate around x, y, z (counterclockwise when looking towards the origin)
%

function R = rotate_mat(x, y, z)
% It is assumed that this matrix is muplied from the hind, rotating objects
% along x-axis, y-axis, and z-axis in this order.

Rx = [      1       0       0; ...
            0  cos(x) -sin(x); ...
            0  sin(x)  cos(x)];

Ry = [ cos(y)       0  sin(y); ...
            0       1       0; ...
      -sin(y)       0  cos(y)];

Rz = [ cos(z) -sin(z)       0; ...
       sin(z)  cos(z)       0; ...
            0      0        1];

R = Rz*Ry*Rx;

return;


function [vals, locs] = minandmax3(f)
%MINANDMAX3   returns the global minimum and maximum value of a CHEBFUN3.
%   VALS = minandmax3(F) returns the global minimum and maximum value of a
%   CHEBFUN3 object F over its domain. VALS is a vector of length 2 such
%   that Y(1) = min3(F(x,y,z)) and Y(2) = max3(F(x,y,z)).
%
%   [VALS, LOCS] = minandmax3(F) also returns the position of the global
%   minimum and maximum.
%
% See also CHEBFUN3/MAX, CHEBFUN3/MAX2, CHEBFUN3/NORM.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% check for empty CHEBFUN3.
if ( isempty(f) )
    vals = [];
    locs = [];
    return
end

doNewton = 1;   % Newton polishing?

% Extract low rank representation of f:
[fCore, fCols, fRows, fTubes] = tucker(f);
dom = f.domain;

% Is f the zero function?
if ( iszero(f) )
    locs = [(dom(1) + dom(2))/2  (dom(3) + dom(4))/2  (dom(5) + dom(6))/2];
    locs = [locs; locs];
    vals = [0; 0];
end

[m, n, p] = length(f);
if ( ndf(f) > 5e4 )
    m = min(m, 129);
    n = min(n, 129);
    p = min(p, 129);
end

% We seek a fast initial guess. So we first discretize the object.
xpts = chebpts(m, fCols.domain);
ypts = chebpts(n, fRows.domain);
zpts = chebpts(p, fTubes.domain);
colVals = feval(fCols, xpts);
rowVals = feval(fRows, ypts);
tubeVals = feval(fTubes, zpts);
T = chebfun3.txm(chebfun3.txm(chebfun3.txm(fCore,colVals,1), rowVals,2), ...
    tubeVals,3);

% Minimum entry in the sample:
[ignored, ind] = min(T(:));
[col, row, tub] = ind2sub(size(T), ind);
loc(1,1) = xpts(col);
loc(1,2) = ypts(row);
loc(1,3) = zpts(tub);
vals(1) = feval(f, loc(1, 1), loc(1, 2), loc(1, 3));

% Maximum entry in the sample:
[ignored, ind] = max(T(:));
[col, row, tub] = ind2sub(size(T), ind);
loc(2,1) = xpts(col);
loc(2,2) = ypts(row);
loc(2,3) = zpts(tub);
vals(2) = feval(f, loc(2, 1), loc(2, 2), loc(2, 3));

% Get more digits with optimisation algorithms.
lb = [dom(1); dom(3); dom(5)];
ub = [dom(2); dom(4); dom(6)];

if ( ~isempty(ver('optim')) )
% Matlab's Optimization Toolbox is available. So, use fmincon command.
    options = optimset('Display', 'none', 'TolFun', eps, 'TolX', eps, ...
        'algorithm', 'active-set');
    [minLoc, vals(1)] = fmincon(@(x) feval(f, x(1), x(2), x(3)), ...
        loc(1, :), [], [], [], [], lb, ub, [], options);

    [maxLoc, vals(2)] = fmincon(@(x) -feval(f, x(1), x(2), x(3)), ...
        loc(2,:), [], [], [], [], lb, ub, [], options);
    vals(2) = -vals(2);
    loc(1,:) = minLoc;
    loc(2,:) = maxLoc;
    locs = loc;
else
    try
        % Use core Matlab fminsearch command by converting to an
        % unconstrained optimization problem.
        % Maps from [-1, 1] to dom(1:2), dom(3:4), and dom(5:6)
        % respectively.
        map1 = bndfun.createMap(dom(1:2));
        map2 = bndfun.createMap(dom(3:4));
        map3 = bndfun.createMap(dom(5:6));

        % Unconstrained initial guesses:
        loc(:, 1) = asin(map1.Inv(loc(:, 1)));
        loc(:, 2) = asin(map2.Inv(loc(:, 2)));
        loc(:, 3) = asin(map3.Inv(loc(:, 3)));

        % Maps from R to dom(1:2), dom(3:4), and dom(5:6) respectively.
        map1 = @(x) map1.For(sin(x));
        map2 = @(x) map2.For(sin(x));
        map3 = @(x) map3.For(sin(x));

        % Set options:
        options = optimset('Display', 'off', 'TolFun', eps, 'TolX', eps);
        warnstate = warning;
        warning('off'); % Disable verbose warnings from fminsearch.
        f_mapped = @(x) feval(f, map1(x(1)), map2(x(2)),map3(x(3)));

        [minLoc, vals(1)] = fminsearch(@(x) f_mapped(x), loc(1, :), options);
        [maxLoc, vals(2)] = fminsearch(@(x) -f_mapped(x), loc(2, :), options);

        vals(2) = -vals(2);
        loc(1:2, 1) = map1([minLoc(1); maxLoc(1)]);
        loc(1:2, 2) = map2([minLoc(2); maxLoc(2)]);
        loc(1:2, 3) = map3([minLoc(3); maxLoc(3)]);
        warning(warnstate);
        locs = loc;

    catch
        % Nothing is going to work. So we will have to go with initial
        % guesses.
    end
end

if ( doNewton )
    % Store values before applying any Newton iterations to restore if
    % Newton was diverging.
    locsOld = locs;
    valsOld = vals;

    % If the global max or min is already on the edge or out of domain,
    % do NOT apply Newton iteration:
    if ( isOutOfDomain(locs, dom) )
        return
    end

    % A few steps of Newton for optimization (involves computing Hessian)
    H = Hessian(f);
    gradF = grad(f);
    gradF = @(x,y,z) gradF(x,y,z);

    % Disable verbose warnings from Newton step if e.g., the Hessian
    % matrix is singular which happens e.g., if a 2D function is passed to
    % minandmax3.
    warning_state = warning('off','MATLAB:singularMatrix');
	% Use try-catch to guarantee original warning state is restored.
    try
        lastwarn('')
        k = 1; % k Newton iterations:
        for iter = 1:k
            locs(1,:) = newton_opt(locs(1,:), H, gradF);
            vals(:,1) = feval(f, locs(1, 1), locs(1, 2), locs(1, 3));

            locs(2,:) = newton_opt(locs(2, :), H, gradF);
            vals(:,2) = feval(f,locs(2, 1), locs(2, 2), locs(2, 3));
        end
        [ignored, last_warn] = lastwarn;
        if strcmp(last_warn,'MATLAB:singularMatrix')
            % Return the old value if the Hessian matrix was singular.
            locs = locsOld;
            vals = valsOld;
            return
        end
        warning(warning_state)
    catch err
        warning(warning_state)
        rethrow(err)
    end

    % If Newton's method moved us outside of the domain, then return old
    % value.
    if ( isOutOfDomain(locs, dom) )
        locs = locsOld;
        vals = valsOld;
    end

end

end

%%
function H = Hessian(f)
% Forms the Hessian matrix of a CHEBFUN3 object f

H11 = diff(f, 2, 1);
H12 = diff(diff(f, 1, 2), 1, 1);
H13 = diff(diff(f, 1, 3), 1, 1);

H21 = diff(diff(f, 1, 1), 1, 2);
H22 = diff(f, 2, 2);
H23 = diff(diff(f, 1, 3), 1, 2);

H31 = diff(diff(f, 1, 1), 1, 3);
H32 = diff(diff(f, 1, 2), 1, 3);
H33 = diff(f, 2, 3);
H = @(x,y,z) [feval(H11, x, y, z)  feval(H12, x, y, z)  feval(H13, x, y, z);
              feval(H21, x, y, z)  feval(H22, x, y, z)  feval(H23, x, y, z);
              feval(H31, x, y, z)  feval(H32, x, y, z)  feval(H33, x, y, z)];
end

%%
function sol = newton_opt(init, H, gradF)
% Perform one step of multivariate optimization using Newton's method.
x = init(1);
y = init(2);
z = init(3);
rhs = -gradF(x, y, z);
h = H(x,y,z)\rhs;
sol = init.' + h;
end

%%
function check = isOutOfDomain(locs, dom)
% Check whether both points in the rows of LOCS are inside the cuboid DOM.
if ( locs(1, 1) <= dom(1) || locs(1, 1) >=dom(2) || ...
         locs(1, 2) <= dom(3) || locs(1, 2)>=dom(4) || ...
         locs(1, 3) <= dom(5) || locs(1, 3)>=dom(6) )
     check = true;
elseif ( locs(2, 1) <= dom(1) || locs(2, 1) >= dom(2) || ...
             locs(2, 2) <= dom(3) || locs(2, 2) >= dom(4) || ...
             locs(2, 3) <= dom(5) || locs(2, 3) >= dom(6) )
         check = true;
else
    check = false;
end
end
%%


%% Clear and Close Figures
clear ; close all; clc

correctPredictedSpam = 85
incorrectPredictedSpam = 890
correctPredictedNoSpam = 10
incorrectPredictedNoSpam = 15

total = correctPredictedSpam + incorrectPredictedSpam + correctPredictedNoSpam + incorrectPredictedNoSpam

accuracy = ( correctPredictedSpam + correctPredictedNoSpam ) / total
precision = correctPredictedSpam / (correctPredictedSpam + incorrectPredictedSpam)
recall = correctPredictedSpam / (correctPredictedSpam + incorrectPredictedNoSpam)
F1 = (2 * precision * recall) / (precision + recall)
F1