function [h,miss,stds] = plotRoc( D, varargin )
% Function for display of rocs (receiver operator characteristic curves).
%
% Display roc curves. Consistent usage ensures uniform look for rocs. The
% input D should have n rows, each of which is of the form:
%  D = [falsePosRate truePosRate]
% D is generated, for example, by scanning a detection threshold over n
% values from 0 (so first entry is [1 1]) to 1 (so last entry is [0 0]).
% Alternatively D can be a cell vector of rocs, in which case an average
% ROC will be shown with error bars. Plots missRate (which is just 1 minus
% the truePosRate) on the y-axis versus the falsePosRate on the x-axis.
%
% USAGE
%  [h,miss,stds] = plotRoc( D, prm )
%
% INPUTS
%  D    - [nx2] n data points along roc [falsePosRate truePosRate]
%         typically ranges from [1 1] to [0 0] (or may be reversed)
%  prm  - [] param struct
%   .color    - ['g'] color for curve
%   .lineSt   - ['-'] linestyle (see LineSpec)
%   .lineWd   - [4] curve width
%   .logx     - [0] use logarithmic scale for x-axis
%   .logy     - [0] use logarithmic scale for y-axis
%   .marker   - [''] marker type (see LineSpec)
%   .mrkrSiz  - [12] marker size
%   .nMarker  - [5] number of markers (regularly spaced) to display
%   .lims     - [0 1 0 1] axes limits
%   .smooth   - [0] if T compute lower envelop of roc to smooth staircase
%   .fpTarget - [] return miss rates at given fp values (and draw lines)
%   .xLbl     - ['false positive rate'] label for x-axis
%   .yLbl     - ['miss rate'] label for y-axis
%
% OUTPUTS
%  h    - plot handle for use in legend only
%  miss - average miss rates at fpTarget reference values
%  stds - standard deviation of miss rates at fpTarget reference values
%
% EXAMPLE
%  k=2; x=0:.0001:1; data1 = [1-x; (1-x.^k).^(1/k)]';
%  k=3; x=0:.0001:1; data2 = [1-x; (1-x.^k).^(1/k)]';
%  hs(1)=plotRoc(data1,struct('color','g','marker','s'));
%  hs(2)=plotRoc(data2,struct('color','b','lineSt','--'));
%  legend( hs, {'roc1','roc2'} ); xlabel('fp'); ylabel('fn');
%
% See also
%
% Piotr's Computer Vision Matlab Toolbox      Version 3.02
% Copyright 2014 Piotr Dollar.  [pdollar-at-gmail.com]
% Licensed under the Simplified BSD License [see external/bsd.txt]

% get params
[color,lineSt,lineWd,logx,logy,marker,mrkrSiz,nMarker,lims,smooth, ...
  fpTarget,xLbl,yLbl] = getPrmDflt( varargin, {'color' 'g' 'lineSt' '-' ...
  'lineWd' 4 'logx' 0 'logy' 0 'marker' '' 'mrkrSiz' 12 'nMarker' 5 ...
  'lims' [] 'smooth' 0 'fpTarget' [] 'xLbl' 'false positive rate' ...
  'yLbl' 'miss rate' } );
if( isempty(lims) ); lims=[logx*1e-5 1 logy*1e-5 1]; end

% ensure descending fp rate, change to miss rate, optionally 'nicefy' roc
if(~iscell(D)), D={D}; end; nD=length(D);
for j=1:nD, assert(size(D{j},2)==2); end
for j=1:nD, if(D{j}(1,2)<D{j}(end,2)), D{j}=flipud(D{j}); end; end
for j=1:nD, D{j}(:,2)=1-D{j}(:,2); assert(all(D{j}(:,2)>=0)); end
if(smooth), for j=1:nD, D{j}=smoothRoc(D{j}); end; end

% plot: (1) h for legend only, (2) markers, (3) error bars, (4) roc curves
hold on; axis(lims); xlabel(xLbl); ylabel(yLbl);
prmMrkr = {'MarkerSize',mrkrSiz,'MarkerFaceColor',color};
prmClr={'Color',color}; prmPlot = [prmClr,{'LineWidth',lineWd}];
h = plot( 2, 0, [lineSt marker], prmMrkr{:}, prmPlot{:} ); %(1)
DQ = quantizeRocs( D, nMarker, logx, lims ); DQm=mean(DQ,3);
if(~isempty(marker))
  plot(DQm(:,1),DQm(:,2),marker,prmClr{:},prmMrkr{:} ); end %(2)
if(nD>1), DQs=std(DQ,0,3);
  errorbar(DQm(:,1),DQm(:,2),DQs(:,2),'.',prmClr{:}); end %(3)
if(nD==1), DQ=D{1}; else DQ=quantizeRocs(D,100,logx,lims); end
DQm = mean(DQ,3); plot( DQm(:,1), DQm(:,2), lineSt, prmPlot{:} ); %(4)

% plot line at given fp rate
m=length(fpTarget); miss=zeros(1,m); stds=miss;
if( m>0 )
  assert( min(DQm(:,1))<=min(fpTarget) ); DQs=std(DQ,0,3);
  for i=1:m, j=find(DQm(:,1)<=fpTarget(i)); j=j(1);
    miss(i)=DQm(j,2); stds(i)=DQs(j,2); end
  fp=min(fpTarget); plot([fp fp],lims(3:4),'Color',.7*[1 1 1]);
  fp=max(fpTarget); plot([fp fp],lims(3:4),'Color',.7*[1 1 1]);
end

% set log axes
if( logx==1 )
  ticks=10.^(-8:8);
  set(gca,'XScale','log','XTick',ticks);
end
if( logy==1 )
  ticks=[.001 .002 .005 .01 .02 .05 .1 .2 .5 1];
  set(gca,'YScale','log','YTick',ticks);
end
if( logx==1 || logy==1 ), grid on;
  set(gca,'XMinorGrid','off','XMinorTic','off');
  set(gca,'YMinorGrid','off','YMinorTic','off');
end

end

function DQ = quantizeRocs( Ds, nPnts, logx, lims )
% estimate miss rate at each target fp rate
nD=length(Ds); DQ=zeros(nPnts,2,nD);
if(logx==1), fps=logspace(log10(lims(1)),log10(lims(2)),nPnts);
else fps=linspace(lims(1),lims(2),nPnts); end; fps=flipud(fps');
for j=1:nD, D=[Ds{j}; 0 1]; k=1; fp=D(k,1);
  for i=1:nPnts
    while( k<size(D,1) && fp>=fps(i) ), k=k+1; fp=D(k,1); end
    k0=max(k-1,1); fp0=D(k0,1); assert(fp0>=fp);
    if(fp0==fp), r=.5; else r=(fps(i)-fp)/(fp0-fp); end
    DQ(i,1,j)=fps(i); DQ(i,2,j)=r*D(k0,2)+(1-r)*D(k,2);
  end
end
end

function D1 = smoothRoc( D )
D1 = zeros(size(D));
n = size(D,1); cnt=0;
for i=1:n
  isAnkle = (i==1) || (i==n);
  if( ~isAnkle )
    dP=D1(cnt,:); dC=D(i,:); dN=D(i+1,:);
    isAnkle = (dC(1)~=dP(1)) && (dC(2)~=dN(2));
  end
  if(isAnkle); cnt=cnt+1; D1(cnt,:)=D(i,:); end
end
D1=D1(1:cnt,:);
end


function plot_accuracy_comparison

color = {'r', 'g'};
leng = {'PoseCNN', 'PoseCNN New'};
aps = zeros(2, 1);
lengs = cell(2, 1);
close all;

% load results
object = load('results_comparison.mat');
distances_sys = object.distances_sys;
distances_non = object.distances_non;
rotations = object.errors_rotation;
translations = object.errors_translation;
cls_ids = object.results_cls_id;

index_plot = [1, 2];

% read class names
fid = fopen('classes.txt', 'r');
C = textscan(fid, '%s');
classes = C{1};
classes{end+1} = 'All 21 objects';
fclose(fid);

hf = figure('units','normalized','outerposition',[0 0 1 1]);
font_size = 24;
max_distance = 0.1;

% for each class
for k = 1:numel(classes)
    index = find(cls_ids == k);
    if isempty(index)
        index = 1:size(distances_sys,1);
    end

    % distance symmetry
    subplot(2, 2, 1);
    for i = index_plot
        D = distances_sys(index, i);
        D(D > max_distance) = inf;
        d = sort(D);
        n = numel(d);
        accuracy = cumsum(ones(1, n)) / n;
        plot(d, accuracy, color{i}, 'LineWidth', 4);
        aps(i) = VOCap(d, accuracy);
        lengs{i} = sprintf('%s (%.2f)', leng{i}, aps(i) * 100);
        hold on;
    end
    hold off;
    %h = legend('network', 'refine tranlation only', 'icp', 'stereo translation only', 'stereo full', '3d coordinate');
    %set(h, 'FontSize', 16);
    h = legend(lengs(index_plot), 'Location', 'southeast');
    set(h, 'FontSize', font_size);
    h = xlabel('Average distance threshold in meter (symmetry)');
    set(h, 'FontSize', font_size);
    h = ylabel('accuracy');
    set(h, 'FontSize', font_size);
    h = title(classes{k}, 'Interpreter', 'none');
    set(h, 'FontSize', font_size);
    xt = get(gca, 'XTick');
    set(gca, 'FontSize', font_size)

    % distance non-symmetry
    subplot(2, 2, 2);
    for i = index_plot
        D = distances_non(index, i);
        D(D > max_distance) = inf;
        d = sort(D);
        n = numel(d);
        accuracy = cumsum(ones(1, n)) / n;
        plot(d, accuracy, color{i}, 'LineWidth', 4);
        aps(i) = VOCap(d, accuracy);
        lengs{i} = sprintf('%s (%.2f)', leng{i}, aps(i) * 100);
        hold on;
    end
    hold off;
    %h = legend('network', 'refine tranlation only', 'icp', 'stereo translation only', 'stereo full', '3d coordinate');
    %set(h, 'FontSize', 16);
    h = legend(lengs(index_plot), 'Location', 'southeast');
    set(h, 'FontSize', font_size);
    h = xlabel('Average distance threshold in meter (non-symmetry)');
    set(h, 'FontSize', font_size);
    h = ylabel('accuracy');
    set(h, 'FontSize', font_size);
    h = title(classes{k}, 'Interpreter', 'none');
    set(h, 'FontSize', font_size);
    xt = get(gca, 'XTick');
    set(gca, 'FontSize', font_size)

    % rotation
    subplot(2, 2, 3);
    for i = index_plot
        D = rotations(index, i);
        d = sort(D);
        n = numel(d);
        accuracy = cumsum(ones(1, n)) / n;
        plot(d, accuracy, color{i}, 'LineWidth', 4);
        hold on;
    end
    hold off;
    %h = legend('network', 'refine tranlation only', 'icp', 'stereo translation only', 'stereo full', '3d coordinate');
    %set(h, 'FontSize', 16);
    h = legend(leng(index_plot), 'Location', 'southeast');
    set(h, 'FontSize', font_size);
    h = xlabel('Rotation angle threshold');
    set(h, 'FontSize', font_size);
    h = ylabel('accuracy');
    set(h, 'FontSize', font_size);
    h = title(classes{k}, 'Interpreter', 'none');
    set(h, 'FontSize', font_size);
    xt = get(gca, 'XTick');
    set(gca, 'FontSize', font_size)

    % translation
    subplot(2, 2, 4);
    for i = index_plot
        D = translations(index, i);
        D(D > max_distance) = inf;
        d = sort(D);
        n = numel(d);
        accuracy = cumsum(ones(1, n)) / n;
        plot(d, accuracy, color{i}, 'LineWidth', 4);
        hold on;
    end
    hold off;
    h = legend(leng(index_plot), 'Location', 'southeast');
    set(h, 'FontSize', font_size);
    h = xlabel('Translation threshold in meter');
    set(h, 'FontSize', font_size);
    h = ylabel('accuracy');
    set(h, 'FontSize', font_size);
    h = title(classes{k}, 'Interpreter', 'none');
    set(h, 'FontSize', font_size);
    xt = get(gca, 'XTick');
    set(gca, 'FontSize', font_size)

    filename = sprintf('plots/%s.png', classes{k});
    hgexport(hf, filename, hgexport('factorystyle'), 'Format', 'png');
end

function ap = VOCap(rec, prec)

index = isfinite(rec);
rec = rec(index);
prec = prec(index)';

mrec=[0 ; rec ; 0.1];
mpre=[0 ; prec ; prec(end)];
for i = 2:numel(mpre)
    mpre(i) = max(mpre(i), mpre(i-1));
end
i = find(mrec(2:end) ~= mrec(1:end-1)) + 1;
ap = sum((mrec(i) - mrec(i-1)) .* mpre(i)) * 10;


function h = plot_embedded_graph( vertex,edge,varargin)
i = edge(:,1);
j = edge(:,2);

% Create a long, NaN-separated list of line segments,
% rather than individual segments.

X = [ vertex(i,1) vertex(j,1) repmat(NaN,size(i))]';
Y = [ vertex(i,2) vertex(j,2) repmat(NaN,size(i))]';
Z = [ vertex(i,3) vertex(j,3) repmat(NaN,size(i))]';
X = X(:);
Y = Y(:);
Z = Z(:);

h = plot3(X, Y, Z, varargin{:} );


%
% output = bilateralFilter( data, edge, ...
%                          edgeMin, edgeMax, ...
%                          sigmaSpatial, sigmaRange, ...
%                          samplingSpatial, samplingRange )
%
% Bilateral and Cross-Bilateral Filter using the Bilateral Grid.
%
% Bilaterally filters the image 'data' using the edges in the image 'edge'.
% If 'data' == 'edge', then it the standard bilateral filter.
% Otherwise, it is the 'cross' or 'joint' bilateral filter.
% For convenience, you can also pass in [] for 'edge' for the normal
% bilateral filter.
%
% Note that for the cross bilateral filter, data does not need to be
% defined everywhere.  Undefined values can be set to 'NaN'.  However, edge
% *does* need to be defined everywhere.
%
% data and edge should be of the greyscale, double-precision floating point
% matrices of the same size (i.e. they should be [ height x width ])
%
% data is the only required argument
%
% edgeMin and edgeMax specifies the min and max values of 'edge' (or 'data'
% for the normal bilateral filter) and is useful when the input is in a
% range that's not between 0 and 1.  For instance, if you are filtering the
% L channel of an image that ranges between 0 and 100, set edgeMin to 0 and
% edgeMax to 100.
%
% edgeMin defaults to min( edge( : ) ) and edgeMax defaults to max( edge( : ) ).
% This is probably *not* what you want, since the input may not span the
% entire range.
%
% sigmaSpatial and sigmaRange specifies the standard deviation of the space
% and range gaussians, respectively.
% sigmaSpatial defaults to min( width, height ) / 16
% sigmaRange defaults to ( edgeMax - edgeMin ) / 10.
%
% samplingSpatial and samplingRange specifies the amount of downsampling
% used for the approximation.  Higher values use less memory but are also
% less accurate.  The default and recommended values are:
%
% samplingSpatial = sigmaSpatial
% samplingRange = sigmaRange
%

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

function output = bilateralFilter( data, edge, edgeMin, edgeMax, sigmaSpatial, sigmaRange, ...
    samplingSpatial, samplingRange )

if( ndims( data ) > 2 ),
    error( 'data must be a greyscale image with size [ height, width ]' );
end

if( ~isa( data, 'double' ) ),
    error( 'data must be of class "double"' );
end

if ~exist( 'edge', 'var' ),
    edge = data;
elseif isempty( edge ),
    edge = data;
end

if( ndims( edge ) > 2 ),
    error( 'edge must be a greyscale image with size [ height, width ]' );
end

if( ~isa( edge, 'double' ) ),
    error( 'edge must be of class "double"' );
end

inputHeight = size( data, 1 );
inputWidth = size( data, 2 );

if ~exist( 'edgeMin', 'var' ),
    edgeMin = min( edge( : ) );
    warning( 'edgeMin not set!  Defaulting to: %f\n', edgeMin );
end

if ~exist( 'edgeMax', 'var' ),
    edgeMax = max( edge( : ) );
    warning( 'edgeMax not set!  Defaulting to: %f\n', edgeMax );
end

edgeDelta = edgeMax - edgeMin;

if ~exist( 'sigmaSpatial', 'var' ),
    sigmaSpatial = min( inputWidth, inputHeight ) / 16;
    fprintf( 'Using default sigmaSpatial of: %f\n', sigmaSpatial );
end

if ~exist( 'sigmaRange', 'var' ),
    sigmaRange = 0.1 * edgeDelta;
    fprintf( 'Using default sigmaRange of: %f\n', sigmaRange );
end

if ~exist( 'samplingSpatial', 'var' ),
    samplingSpatial = sigmaSpatial;
end

if ~exist( 'samplingRange', 'var' ),
    samplingRange = sigmaRange;
end

if size( data ) ~= size( edge ),
    error( 'data and edge must be of the same size' );
end

% parameters
derivedSigmaSpatial = sigmaSpatial / samplingSpatial;
derivedSigmaRange = sigmaRange / samplingRange;

paddingXY = floor( 2 * derivedSigmaSpatial ) + 1;
paddingZ = floor( 2 * derivedSigmaRange ) + 1;

% allocate 3D grid
downsampledWidth = floor( ( inputWidth - 1 ) / samplingSpatial ) + 1 + 2 * paddingXY;
downsampledHeight = floor( ( inputHeight - 1 ) / samplingSpatial ) + 1 + 2 * paddingXY;
downsampledDepth = floor( edgeDelta / samplingRange ) + 1 + 2 * paddingZ;

gridData = zeros( downsampledHeight, downsampledWidth, downsampledDepth );
gridWeights = zeros( downsampledHeight, downsampledWidth, downsampledDepth );

% compute downsampled indices
[ jj, ii ] = meshgrid( 0 : inputWidth - 1, 0 : inputHeight - 1 );

% ii =
% 0 0 0 0 0
% 1 1 1 1 1
% 2 2 2 2 2

% jj =
% 0 1 2 3 4
% 0 1 2 3 4
% 0 1 2 3 4

% so when iterating over ii( k ), jj( k )
% get: ( 0, 0 ), ( 1, 0 ), ( 2, 0 ), ... (down columns first)

di = round( ii / samplingSpatial ) + paddingXY + 1;
dj = round( jj / samplingSpatial ) + paddingXY + 1;
dz = round( ( edge - edgeMin ) / samplingRange ) + paddingZ + 1;

% perform scatter (there's probably a faster way than this)
% normally would do downsampledWeights( di, dj, dk ) = 1, but we have to
% perform a summation to do box downsampling
for k = 1 : numel( dz ),

    dataZ = data( k ); % traverses the image column wise, same as di( k )
    if ~isnan( dataZ  ),

        dik = di( k );
        djk = dj( k );
        dzk = dz( k );

        gridData( dik, djk, dzk ) = gridData( dik, djk, dzk ) + dataZ;
        gridWeights( dik, djk, dzk ) = gridWeights( dik, djk, dzk ) + 1;

    end
end

% make gaussian kernel
kernelWidth = 2 * derivedSigmaSpatial + 1;
kernelHeight = kernelWidth;
kernelDepth = 2 * derivedSigmaRange + 1;

halfKernelWidth = floor( kernelWidth / 2 );
halfKernelHeight = floor( kernelHeight / 2 );
halfKernelDepth = floor( kernelDepth / 2 );

[gridX, gridY, gridZ] = meshgrid( 0 : kernelWidth - 1, 0 : kernelHeight - 1, 0 : kernelDepth - 1 );
gridX = gridX - halfKernelWidth;
gridY = gridY - halfKernelHeight;
gridZ = gridZ - halfKernelDepth;
gridRSquared = ( gridX .* gridX + gridY .* gridY ) / ( derivedSigmaSpatial * derivedSigmaSpatial ) + ( gridZ .* gridZ ) / ( derivedSigmaRange * derivedSigmaRange );
kernel = exp( -0.5 * gridRSquared );

% convolve
blurredGridData = convn( gridData, kernel, 'same' );
blurredGridWeights = convn( gridWeights, kernel, 'same' );

% divide
blurredGridWeights( blurredGridWeights == 0 ) = -2; % avoid divide by 0, won't read there anyway
normalizedBlurredGrid = blurredGridData ./ blurredGridWeights;
normalizedBlurredGrid( blurredGridWeights < -1 ) = 0; % put 0s where it's undefined

% for debugging
% blurredGridWeights( blurredGridWeights < -1 ) = 0; % put zeros back

% upsample
[ jj, ii ] = meshgrid( 0 : inputWidth - 1, 0 : inputHeight - 1 ); % meshgrid does x, then y, so output arguments need to be reversed
% no rounding
di = ( ii / samplingSpatial ) + paddingXY + 1;
dj = ( jj / samplingSpatial ) + paddingXY + 1;
dz = ( edge - edgeMin ) / samplingRange + paddingZ + 1;

% interpn takes rows, then cols, etc
% i.e. size(v,1), then size(v,2), ...
output = interpn( normalizedBlurredGrid, di, dj, dz );


function cS = olivine(cs)
% olivine crystal shape

if nargin == 0
  cs = crystalSymmetry('mmm',[4.762 10.225 5.994],'mineral','olivine');
end

N = Miller({1,1,0},{1,2,0},{0,1,0},{1,0,1},{0,2,1},{0,0,1},cs);

cS = crystalShape(N,2.0,[6.0,1.0,6.0]);

end


function f_x = ParFor15(in1)
%PARFOR15
%    F_X = PARFOR15(IN1)

%    This function was generated by the Symbolic Math Toolbox version 8.2.
%    20-Sep-2019 09:35:38

u = in1(:,1);
ux = in1(:,4);
uxxx = in1(:,6);
f_x = ((ux.*-2.895413584232186e19+uxxx.*2.08074189217307e18+u.*ux.*2.276967144755718e19).*-1.483125065871422e-16)./u;


function eval_tracker(seqs, trackers, eval_type, name_tracker_all, tmp_mat_path, path_anno, rp_all, norm_dst)
% evaluate each tracker
num_tracker = numel(trackers);

threshold_set_overlap = 0:0.05:1;
threshold_set_error   = 0:50;
if norm_dst
    threshold_set_error = threshold_set_error / 100;
end

for i = 1:numel(seqs) % for each sequence
    s    = seqs{i};      % name of sequence

    % load GT and the absent flags
    anno        = dlmread([path_anno s '.txt']);
    absent_anno = dlmread([path_anno 'absent/' s '.txt']);

    for k = 1:num_tracker  % evaluate each tracker
        t = trackers{k};   % name of tracker

        % load tracking result
        res = dlmread([rp_all t.name '_tracking_result/' s '.txt']);
        fprintf(['evaluating ' t.name ' on ' s ' ...\n']);

        success_num_overlap = zeros(1, numel(threshold_set_overlap));
        success_num_err     = zeros(1, numel(threshold_set_error));

        if isempty(res)
            break;
        end

        [err_coverage, err_center] = calc_seq_err_robust(res, anno, absent_anno, norm_dst);

        for t_idx = 1:numel(threshold_set_overlap)
            success_num_overlap(1, t_idx) = sum(err_coverage > threshold_set_overlap(t_idx));
        end

        for t_idx = 1:length(threshold_set_error)
            success_num_err(1, t_idx) = sum(err_center <= threshold_set_error(t_idx));
        end

        len_all = size(anno, 1);  % number of frames in the sequence

        ave_success_rate_plot(k, i, :)     = success_num_overlap/(len_all + eps);
        ave_success_rate_plot_err(k, i, :) = success_num_err/(len_all + eps);
    end
end

% save results
if ~exist(tmp_mat_path, 'dir')
    mkdir(tmp_mat_path);
end

dataName1 = [tmp_mat_path 'aveSuccessRatePlot_' num2str(num_tracker) 'alg_overlap_' eval_type '.mat'];
save(dataName1, 'ave_success_rate_plot', 'name_tracker_all');

dataName2 = [tmp_mat_path 'aveSuccessRatePlot_' num2str(num_tracker) 'alg_error_' eval_type '.mat'];
ave_success_rate_plot = ave_success_rate_plot_err;
save(dataName2, 'ave_success_rate_plot', 'name_tracker_all');

end


function d2Vlims = opf_vlim_hess(x, lambda, mpc, idx, mpopt)
%OPF_VLIM_HESS  Evaluates Hessian of voltage magnitudes.
%   D2VLIMS = OPF_VLIM_HESS(X, LAMBDA, MPC, IDX, MPOPT)
%
%   Hessian evaluation function for voltage magnitudes.
%
%   Inputs:
%     X : optimization vector
%     LAMBDA : column vector of Lagrange multipliers on active and reactive
%              power balance constraints
%     MPC : MATPOWER case struct
%     IDX : index of buses whose voltage magnitudes should be fixed
%     MPOPT : MATPOWER options struct
%
%   Outputs:
%     D2VLIMS : Hessian of voltage magnitudes.
%
%   Example:
%       d2Vlims = opf_vlim_hess(x, lambda, mpc, idx, mpopt);
%
%   See also OPF_VLIM_FCN.

%   MATPOWER
%   Copyright (c) 2018, Power Systems Engineering Research Center (PSERC)
%   by Baljinnyam Sereeter, Delft University of Technology
%   and Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%% unpack data
[Vr, Vi] = deal(x{:});

%% problem dimensions
nb = length(Vi);            %% number of buses
n = length(idx);            %% number of buses with voltage limits

%%----- evaluate Hessian of voltage limit constraints -----
nlam = length(lambda) / 2;
if nlam
    lam = lambda(nlam+1:2*nlam) - lambda(1:nlam);   %% lamVmax - lamVmin
else    %% keep dimensions of empty matrices/vectors compatible
    lam = zeros(0,1);
end

dlam = sparse(idx, idx, 2*lam, nb, nb);
zz = sparse(nb, nb);

%% construct Hessian
d2Vlims = [dlam zz; zz dlam];


function [X, fX, i] = fmincg(f, X, options, P1, P2, P3, P4, P5)
% Minimize a continuous differentialble multivariate function. Starting point
% is given by "X" (D by 1), and the function named in the string "f", must
% return a function value and a vector of partial derivatives. The Polack-
% Ribiere flavour of conjugate gradients is used to compute search directions,
% and a line search using quadratic and cubic polynomial approximations and the
% Wolfe-Powell stopping criteria is used together with the slope ratio method
% for guessing initial step sizes. Additionally a bunch of checks are made to
% make sure that exploration is taking place and that extrapolation will not
% be unboundedly large. The "length" gives the length of the run: if it is
% positive, it gives the maximum number of line searches, if negative its
% absolute gives the maximum allowed number of function evaluations. You can
% (optionally) give "length" a second component, which will indicate the
% reduction in function value to be expected in the first line-search (defaults
% to 1.0). The function returns when either its length is up, or if no further
% progress can be made (ie, we are at a minimum, or so close that due to
% numerical problems, we cannot get any closer). If the function terminates
% within a few iterations, it could be an indication that the function value
% and derivatives are not consistent (ie, there may be a bug in the
% implementation of your "f" function). The function returns the found
% solution "X", a vector of function values "fX" indicating the progress made
% and "i" the number of iterations (line searches or function evaluations,
% depending on the sign of "length") used.
%
% Usage: [X, fX, i] = fmincg(f, X, options, P1, P2, P3, P4, P5)
%
% See also: checkgrad
%
% Copyright (C) 2001 and 2002 by Carl Edward Rasmussen. Date 2002-02-13
%
%
% (C) Copyright 1999, 2000 & 2001, Carl Edward Rasmussen
%
% Permission is granted for anyone to copy, use, or modify these
% programs and accompanying documents for purposes of research or
% education, provided this copyright notice is retained, and note is
% made of any changes that have been made.
%
% These programs and documents are distributed without any warranty,
% express or implied.  As the programs were written for research
% purposes only, they have not been tested to the degree that would be
% advisable in any important application.  All use of these programs is
% entirely at the user's own risk.
%
% [ml-class] Changes Made:
% 1) Function name and argument specifications
% 2) Output display
%

% Read options
if exist('options', 'var') && ~isempty(options) && isfield(options, 'MaxIter')
    length = options.MaxIter;
else
    length = 100;
end

RHO = 0.01;                            % a bunch of constants for line searches
SIG = 0.5;       % RHO and SIG are the constants in the Wolfe-Powell conditions
INT = 0.1;    % don't reevaluate within 0.1 of the limit of the current bracket
EXT = 3.0;                    % extrapolate maximum 3 times the current bracket
MAX = 20;                         % max 20 function evaluations per line search
RATIO = 100;                                      % maximum allowed slope ratio

argstr = ['feval(f, X'];                      % compose string used to call function
for i = 1:(nargin - 3)
  argstr = [argstr, ',P', int2str(i)];
end
argstr = [argstr, ')'];

if max(size(length)) == 2, red=length(2); length=length(1); else red=1; end
S=['Iteration '];

i = 0;                                            % zero the run length counter
ls_failed = 0;                             % no previous line search has failed
fX = [];
[f1 df1] = eval(argstr);                      % get function value and gradient
i = i + (length<0);                                            % count epochs?!
s = -df1;                                        % search direction is steepest
d1 = -s'*s;                                                 % this is the slope
z1 = red/(1-d1);                                  % initial step is red/(|s|+1)

while i < abs(length)                                      % while not finished
  i = i + (length>0);                                      % count iterations?!

  X0 = X; f0 = f1; df0 = df1;                   % make a copy of current values
  X = X + z1*s;                                             % begin line search
  [f2 df2] = eval(argstr);
  i = i + (length<0);                                          % count epochs?!
  d2 = df2'*s;
  f3 = f1; d3 = d1; z3 = -z1;             % initialize point 3 equal to point 1
  if length>0, M = MAX; else M = min(MAX, -length-i); end
  success = 0; limit = -1;                     % initialize quanteties
  while 1
    while ((f2 > f1+z1*RHO*d1) || (d2 > -SIG*d1)) && (M > 0)
      limit = z1;                                         % tighten the bracket
      if f2 > f1
        z2 = z3 - (0.5*d3*z3*z3)/(d3*z3+f2-f3);                 % quadratic fit
      else
        A = 6*(f2-f3)/z3+3*(d2+d3);                                 % cubic fit
        B = 3*(f3-f2)-z3*(d3+2*d2);
        z2 = (sqrt(B*B-A*d2*z3*z3)-B)/A;       % numerical error possible - ok!
      end
      if isnan(z2) || isinf(z2)
        z2 = z3/2;                  % if we had a numerical problem then bisect
      end
      z2 = max(min(z2, INT*z3),(1-INT)*z3);  % don't accept too close to limits
      z1 = z1 + z2;                                           % update the step
      X = X + z2*s;
      [f2 df2] = eval(argstr);
      M = M - 1; i = i + (length<0);                           % count epochs?!
      d2 = df2'*s;
      z3 = z3-z2;                    % z3 is now relative to the location of z2
    end
    if f2 > f1+z1*RHO*d1 || d2 > -SIG*d1
      break;                                                % this is a failure
    elseif d2 > SIG*d1
      success = 1; break;                                             % success
    elseif M == 0
      break;                                                          % failure
    end
    A = 6*(f2-f3)/z3+3*(d2+d3);                      % make cubic extrapolation
    B = 3*(f3-f2)-z3*(d3+2*d2);
    z2 = -d2*z3*z3/(B+sqrt(B*B-A*d2*z3*z3));        % num. error possible - ok!
    if ~isreal(z2) || isnan(z2) || isinf(z2) || z2 < 0 % num prob or wrong sign?
      if limit < -0.5                               % if we have no upper limit
        z2 = z1 * (EXT-1);                 % the extrapolate the maximum amount
      else
        z2 = (limit-z1)/2;                                   % otherwise bisect
      end
    elseif (limit > -0.5) && (z2+z1 > limit)         % extraplation beyond max?
      z2 = (limit-z1)/2;                                               % bisect
    elseif (limit < -0.5) && (z2+z1 > z1*EXT)       % extrapolation beyond limit
      z2 = z1*(EXT-1.0);                           % set to extrapolation limit
    elseif z2 < -z3*INT
      z2 = -z3*INT;
    elseif (limit > -0.5) && (z2 < (limit-z1)*(1.0-INT))  % too close to limit?
      z2 = (limit-z1)*(1.0-INT);
    end
    f3 = f2; d3 = d2; z3 = -z2;                  % set point 3 equal to point 2
    z1 = z1 + z2; X = X + z2*s;                      % update current estimates
    [f2 df2] = eval(argstr);
    M = M - 1; i = i + (length<0);                             % count epochs?!
    d2 = df2'*s;
  end                                                      % end of line search

  if success                                         % if line search succeeded
    f1 = f2; fX = [fX' f1]';
    fprintf('%s %4i | Cost: %4.6e\r', S, i, f1);
    s = (df2'*df2-df1'*df2)/(df1'*df1)*s - df2;      % Polack-Ribiere direction
    tmp = df1; df1 = df2; df2 = tmp;                         % swap derivatives
    d2 = df1'*s;
    if d2 > 0                                      % new slope must be negative
      s = -df1;                              % otherwise use steepest direction
      d2 = -s'*s;
    end
    z1 = z1 * min(RATIO, d1/(d2-realmin));          % slope ratio but max RATIO
    d1 = d2;
    ls_failed = 0;                              % this line search did not fail
  else
    X = X0; f1 = f0; df1 = df0;  % restore point from before failed line search
    if ls_failed || i > abs(length)          % line search failed twice in a row
      break;                             % or we ran out of time, so we give up
    end
    tmp = df1; df1 = df2; df2 = tmp;                         % swap derivatives
    s = -df1;                                                    % try steepest
    d1 = -s'*s;
    z1 = 1/(1-d1);
    ls_failed = 1;                                    % this line search failed
  end
  if exist('OCTAVE_VERSION')
    fflush(stdout);
  end
end
fprintf('\n');


function [plaza,v,vmax]=random_slow(plaza,v,vmax,probslow);
    [L,W]=size(plaza);
    for lanes=2:W-1;
        temp=find(plaza(:,lanes)==1);
        nn=length(temp);
        for k=1:nn;
            i=temp(k);
            if(rand<=probslow)
                v(i,lanes)=max(v(i,lanes)-1,0);
            end
        end
    end
end


function Population = WOF_NSGAIIIEnvironmentalSelection(Population,N,Z,Zmin)
% -----------------------------------------------------------------------
%  Copyright (C) 2020 Heiner Zille
%
%  This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0
%  International License. (CC BY-NC-SA 4.0). To view a copy of this license,
%  visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or see the
%  pdf-file "License-CC-BY-NC-SA-4.0.pdf" that came with this code.
%
%  You are free to:
%  * Share ? copy and redistribute the material in any medium or format
%  * Adapt ? remix, transform, and build upon the material
%  Under the following terms:
%  * Attribution ? You must give appropriate credit, provide a link to the
%     license, and indicate if changes were made. You may do so in any reasonable
%     manner, but not in any way that suggests the licensor endorses you or your use.
%  * NonCommercial ? You may not use the material for commercial purposes.
%  * ShareAlike ? If you remix, transform, or build upon the material, you must
%    distribute your contributions under the same license as the original.
%  * No additional restrictions ? You may not apply legal terms or technological
%    measures that legally restrict others from doing anything the license permits.
%
%  Author of this Code:
%   Heiner Zille <heiner.zille@ovgu.de> or <heiner.zille@gmail.com>
%
%  This code is based on the following publications:
%
%  1) Heiner Zille
%     "Large-scale Multi-objective Optimisation: New Approaches and a Classification of the State-of-the-Art"
%     PhD Thesis, Otto von Guericke University Magdeburg, 2019
%     http://dx.doi.org/10.25673/32063
%
%  2) Heiner Zille and Sanaz Mostaghim
%     "Comparison Study of Large-scale Optimisation Techniques on the LSMOP Benchmark Functions"
%     IEEE Symposium Series on Computational Intelligence (SSCI), IEEE, Honolulu, Hawaii, November 2017
%     https://ieeexplore.ieee.org/document/8280974
%
%  3) Heiner Zille, Hisao Ishibuchi, Sanaz Mostaghim and Yusuke Nojima
%     "A Framework for Large-scale Multi-objective Optimization based on Problem Transformation"
%     IEEE Transactions on Evolutionary Computation, Vol. 22, Issue 2, pp. 260-275, April 2018.
%     http://ieeexplore.ieee.org/document/7929324
%
%  4) Heiner Zille, Hisao Ishibuchi, Sanaz Mostaghim and Yusuke Nojima
%     "Weighted Optimization Framework for Large-scale Mullti-objective Optimization"
%     Genetic and Evolutionary Computation Conference (GECCO), ACM, Denver, USA, July 2016
%     http://dl.acm.org/citation.cfm?id=2908979
%
%  This file is intended to work with the PlatEMO framework version 2.5.
%  Date of publication of this code: 06.04.2020
%  Last Update of this code: 06.04.2020
%  A newer version of this algorithm may be available. Please contact the author
%  or see http://www.ci.ovgu.de/Research/Codes.html.
%
% The files may have been modified in Feb 2021 by the authors of the Platemo framework to work with the Platemo 3.0 release.
% -----------------------------------------------------------------------
% This file is derived from its original version containied in the PlatEMO
% framework. The original copyright disclaimer can be found below.
% -----------------------------------------------------------------------

% The environmental selection of NSGA-III

    if isempty(Zmin)
        Zmin = ones(1,size(Z,2));
    end

    %% Non-dominated sorting
    [FrontNo,MaxFNo] = NDSort(Population.objs,Population.cons,N);
    Next = FrontNo < MaxFNo;

    %% Select the solutions in the last front
    Last   = find(FrontNo==MaxFNo);
    Choose = LastSelection(Population(Next).objs,Population(Last).objs,N-sum(Next),Z,Zmin);
    Next(Last(Choose)) = true;
    % Population for next generation
    Population = Population(Next);
end

function Choose = LastSelection(PopObj1,PopObj2,K,Z,Zmin)
% Select part of the solutions in the last front

    PopObj = [PopObj1;PopObj2] - repmat(Zmin,size(PopObj1,1)+size(PopObj2,1),1);
    [N,M]  = size(PopObj);
    N1     = size(PopObj1,1);
    N2     = size(PopObj2,1);
    NZ     = size(Z,1);

    %% Normalization
    % Detect the extreme points
    Extreme = zeros(1,M);
    w       = zeros(M)+1e-6+eye(M);
    for i = 1 : M
        [~,Extreme(i)] = min(max(PopObj./repmat(w(i,:),N,1),[],2));
    end
    % Calculate the intercepts of the hyperplane constructed by the extreme
    % points and the axes
    Hyperplane = PopObj(Extreme,:)\ones(M,1);
    a = 1./Hyperplane;
    if any(isnan(a))
        a = max(PopObj,[],1)';
    end
    % Normalization
    PopObj = PopObj./repmat(a',N,1);

    %% Associate each solution with one reference point
    % Calculate the distance of each solution to each reference vector
    Cosine   = 1 - pdist2(PopObj,Z,'cosine');
    Distance = repmat(sqrt(sum(PopObj.^2,2)),1,NZ).*sqrt(1-Cosine.^2);
    % Associate each solution with its nearest reference point
    [d,pi] = min(Distance',[],1);

    %% Calculate the number of associated solutions except for the last front of each reference point
    rho = hist(pi(1:N1),1:NZ);

    %% Environmental selection
    Choose  = false(1,N2);
    Zchoose = true(1,NZ);
    % Select K solutions one by one
    while sum(Choose) < K
        % Select the least crowded reference point
        Temp = find(Zchoose);
        Jmin = find(rho(Temp)==min(rho(Temp)));
        j    = Temp(Jmin(randi(length(Jmin))));
        I    = find(Choose==0 & pi(N1+1:end)==j);
        % Then select one solution associated with this reference point
        if ~isempty(I)
            if rho(j) == 0
                [~,s] = min(d(N1+I));
            else
                s = randi(length(I));
            end
            Choose(I(s)) = true;
            rho(j) = rho(j) + 1;
        else
            Zchoose(j) = false;
        end
    end
end


%% QUAD_JOB_LOCAL divides the quadrature problem into tasks.
%
%  Discussion:
%
%    Each task is given a portion of the interval [0,1] over which
%    to integrate.
%
%    Each task returns the estimated integral over its subinterval.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    27 March 2010
%
%  Author:
%
%    John Burkardt
%
  clear

  fprintf ( 1, '\n' );
  fprintf ( 1, 'QUAD_JOB_LOCAL\n' );
  fprintf ( 1, '  Set up and execute a job of independent tasks.\n' );
  fprintf ( 1, '  The computation of an integral over [0,1] is divided\n' );
  fprintf ( 1, '  into tasks integrating from A to B.\n' );
  fprintf ( 1, '  The integral estimate is the sum of the task results.\n' );
%
%  Define the job.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Define the job:\n' );

  job = createJob ( ...
    'configuration', 'local', ...
    'FileDependencies', { 'quad_task.m' } );

  task_num = 10;
  n = 100001;
  n_per_task = 1 + ( n - 1 ) / task_num;
%
%  Define the tasks that constitute the job.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Define %d tasks:\n', task_num );

  for task = 1 : task_num
    a = ( task - 1 ) / task_num;
    b =   task       / task_num;
    task_id = createTask ( job, @quad_task, 1, { n, a, b } );
  end
%
%  Submit the job.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Submit the job:\n' );

  submit ( job );
%
%  Wait for results.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Wait for job to finish:\n' );

  timestamp ( )
  wait ( job );
  timestamp ( )
%
%  The following commands will capture and print error messages
%  from the tasks, if something bad occurs.
%
  errmsgs = get ( job.Tasks, {'ErrorMessage'} );
  nonempty = ~cellfun ( @isempty, errmsgs );
  celldisp ( errmsgs(nonempty) );
%
%  Retrieve the output from all tasks.
%
  results = getAllOutputArguments ( job );
%
%  The output is a cell array.
%  We much prefer a MATLAB matrix!
%
  results = cell2mat ( results );
%
%  Sum the results to get the estimate.
%
  quad = sum ( results(:,1) );
  exact = pi;
  err = abs ( quad - exact );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Exact integral = %f\n', exact );
  fprintf ( 1, '  Estimate =       %f\n', quad );
  fprintf ( 1, '  Error =          %e\n', err );
%
%  Clean up by freeing memory associated with the job.
%
  destroy ( job );
%
%  Terminate;
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'QUAD_JOB_LOCAL:\n' );
  fprintf ( 1, '  Normal end of execution.\n' );


%Detection
%=========
%
%Calculation of the likelihood ratio of ARMA models.
%
%  likelihoodR         - Likelihood ratio
%  likelihoodR_mod     - modified likelihood ratio: includes
%                        a correction for the number of
%                        estimated parameters. This can be used
%                        for detection with models that have been
%                        selected using statistical order selection
%                        criteria.


function x = p14_start ( n )

%*****************************************************************************80
%
%% P14_START returns a starting point for optimization for problem 14.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    16 March 2000
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the number of variables X.
%
%    Output, real X(N), a starting point for the optimization.
%
  x = zeros ( n, 1 );

  for i = 1 : n
    if ( mod ( i, 2 ) == 1 )
      x(i) = - 1.2;
    else
      x(i) = 1.0;
    end
  end

  return
end


function pf =loadPoleFigure_nja(fname,varargin)

assertExtension(fname,'.nja');

comment = '';
fid = efopen(fname);
try
  % read header
  comment = textscan(fid,'%s',45,'delimiter','&','whitespace','');
  fclose(fid);
catch
  interfaceError(fname,fid);
end

try
  % read data
  d = dlmread(fname,'',21,0);
  th = d(:,1)*degree;
  rh = d(:,2)*degree;
  bg  = d(:,4);
  d  = d(:,3);

  h = string2Miller(regexprep([comment{1}{19:21}], '[A-Z=\s]', '', 'ignorecase'));
  r = vector3d.byPolar(th,rh,'antipodal');

  pf = PoleFigure(h,r,d,'BACKGROUND',bg,varargin{:});
catch
  interfaceError(fname);
end


function modes = gtmlmode(net, data)
%GTMLMODE Mode responsibility for data in a GTM.
%
%	Description
%	 MODES = GTMLMODE(NET, DATA) takes a GTM structure NET, and computes
%	the modes of the responsibility  distributions for each data point in
%	DATA.  These will always lie at one of the latent space sample points
%	NET.X.
%
%	See also
%	GTM, GTMPOST, GTMLMEAN
%

%	Copyright (c) Ian T Nabney (1996-2001)

% Check for consistency
errstring = consist(net, 'gtm', data);
if ~isempty(errstring)
  error(errstring);
end

R = gtmpost(net, data);
% Mode is maximum responsibility
[max_resp, max_index] = max(R, [], 2);
modes = net.X(max_index, :);


function F = acot(F, varargin)
%ACOT   Inverse cotangent of a CHEBFUN.
%   ACOT(F) computes the inverse cotangent of the CHEBFUN F.
%
%   ACOT(F, PREF) does the same but uses the CHEBFUNPREF object PREF when
%   computing the composition.
%
% See also COT, ACOTD.

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

% Call the compose method:
F = compose(F, @acot, varargin{:});

end


function [ status ] = issues( k )
%ISSUES M2T Test cases related to issues
%
% Issue-related test cases for matlab2tikz
%
% See also: ACID, matlab2tikz_acidtest
    testfunction_handles = {
                             @scatter3Plot3
                           };

    numFunctions = length( testfunction_handles );

    if (k<=0)
        status = testfunction_handles;
        return;  % This is used for querying numFunctions.

    elseif (k<=numFunctions)
        status = testfunction_handles{k}();
        status.function = func2str(testfunction_handles{k});

    else
        error('issues:outOfBounds', ...
              'Out of bounds (number of testfunctions=%d)', numFunctions);
    end

end

% =========================================================================
function [stat] = scatter3Plot3()
    stat.description = 'Scatter3 plot with 2 colors';
    stat.issues = 292;

    hold on;
    x = sin(1:5);
    y = cos(3.4 *(1:5));
    z = x.*y;
    scatter3(x,y,z,150,...
             'MarkerEdgeColor','none','MarkerFaceColor','k');
    scatter3(-x,y,z,150,...
             'MarkerEdgeColor','none','MarkerFaceColor','b');
end

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


function [I,p] = LEMS1im_read()
% LEMS1im_read: Read one image for LEMS correction
%
% [I,p] = LEMS1im_read()
% I: selected image
% p: parameters
%         info: 'N/A'   % 'N/A' if not dicom
%         Imin: 2       % for contrast
%         Imax: 255
%         rect: [10.2942 13.0805 113.4776 118.8813] % for croping I0
%     filename: 'arlie.pd.im256gl4.tif' % I0 location
%     pathname: [1x64 char]
%
% Olivier Salvado, Case, 15-nov-06
% v0: from stepA_1 in LEMS 2D pipeline

disp(' ')
disp([' --- Read one image for LEMS1im...'])
% colormap(gray(256)),clf
% set(gcf,'BackingStore','on')
% debug = 0;
% warning off MATLAB:divideByZero

%% ask for the file
wiam = cd;
% cd('C:\Documents and Settings\oxs13\My Documents\0 Main backup\Matlab Codes\Data for experiment')
cd('C:\')
[filename, filepath] = uigetfile('*.*', 'Select image to correct');
cd(wiam);
disp(['     In folder:' filepath])

%% read the file
try
    % --- try to read a Dicom
    info = dicominfo([filepath filename]);
    I0 = dicomread([filepath filename]);
catch
    % --- if there is an error, it must be an image.
    info = 'N/A';
    I0 = imread([filepath filename]);
end

%% check if it is a rgb image
if size(I0,3)>1,
    disp('This image has multiple channels, try to convert to gray')
    I0 = rgb2gray(I0);
end

%% convert to [0...255]
I0 = single(I0);
Imin = min(I0(:));
Imax = max(I0(:));
I0 = I0-Imin;
I = I0/(Imax-Imin)*255;

%% update the outputs
p.info = info;
p.Imin = Imin;
p.Imax = Imax;
p.filename = filename;
p.filepath = filepath;

disp('     ... Data loaded')


function [sCodebook, settings] = grlvq(sCodebook, sData, varargin)

%GRLVQ trains a codebook using the Generalized Relevance LVQ (GRLVQ)
%algorithm.
%
% sM = grlvq(sM, sD, [argID, value, ...])
%
%  sM = grlvq(sM, sD)
%  sM = grlvq(sM, sD, 'learningRatePrototypes', [0.01 0.001], 'relevanceStart', 10);
%
% Required Input Arguments:
%   sM                     (struct) map struct containing the labels as the
%                                   first column of .labels
%   sD                     (struct) data struct containing the labels as
%                                   the first column of .labels
%
% Optional Input Arguments:
%   PrototypesPerClass     (vector) number of prototypes per class used;
%                                   a number or a vector with a number
%                                   for each class (default=1)
%   initialRelevances      (vector) initial Relevances
%   regularization         (scalar) regularization for relevances
%   testSet                (matrix) a test set to compute the test error;
%                                   the last column should be the labels
%   comparable             (scalar) a flag which resets the random
%                                   generator to produce comparable results
%                                   if set to 1
%   optimization           (string) choice for the optimization technique:
%                                   sgd or fminlbfgs (default=fminlbfgs)
%  Parameter for the stochastic gradient descent sgd:
%   nb_epochs              (scalar) the number of epochs (default=100)
%   learningRatePrototypes (vector) learning rate for the prototypes; could
%                                   be the start and end value or a vector
%                                   of length nb_epochs
%   learningRateRelevances (vector) learning rate for the relevances; could
%                                   be the start and end value or a vector
%                                   of length nb_epochs
%   relevanceStart         (scalar) epoch to start the relevance learning
%                                   (default=1)
%  Parameter for the build-in function fminlbfgs:
%   threshstop             (scalar) the training error for early stopping
%                                   (default=0)
%   useEarlyStopping       (scalar) use early stopping based on threshstop
%                                   (default=1)
%   Display                (string) the optimization output 'iter' or 'off'
%                                   (default='off')
%   GradObj                (string) turn the usage of gradient information
%                                   on or off (default='on')
%   HessUpdate             (string) the update can be 'lbfgs', 'bfgs' or
%                                   'steepdesc' (default='lbfgs')
%   TolFun                 (scalar) the tolerance (default=1e-6)
%   MaxIter                (scalar) the maximal number of iterations
%                                   (default=2500)
%   MaxFunEvals                     the maximal number of function
%                                   evaluations (default=1000000)
%   TolX                   (scalar) tolerance on the minimum
%                                   (default=1e-10)
%   DiffMinChange          (scalar) minimal change (default=1e-10)
%
% Output Arguments:
%   sM                     (struct) map struct containing the optimized
%                                   prototypes and the relevances lambda
%   settings               (struct) information on the settings of the
%                                   algorithm
%
% NOTE: does not take the vector mask into account but trains a relevance
%       for each dimension.
%
% For more help, try 'type grlvq', or check out the online documentation.
% See also GMLVQ, GRLVQ_CORE, LVQ3, LVQ1.

%%%%%%%%%%%%% DETAILED DESCRIPTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% grlvq
%
% PURPOSE
%
% Trains a codebook and a global metric with the Generalized Relevance LVQ
% (GRLVQ) algorithm.
%
% SYNTAX
%
%   sM = grlvq(sM, sD)
%   sM = grlvq(sM, sD, 'learningRatePrototypes', [0.01 0.001], 'relevanceStart', 10);
%   [sM, settings] = grlvq(sM, sD, argID, value, ...);
%
% DESCRIPTION
%
% GRLVQ constitutes an enhancement of the LVQ algorithm by minimizing an
% explicit error function and optimizing the metric of the space where the
% computation is performed. Distances are calculated by
%   d(x,y) = (x - y) * diag(lambda) * (x - y)'.
% The vector lambda is optimized by the algorithm.
%
% Two optimization techniques are implemented:
%     - stochastic gradient descent (sgd)
%     - limited memory Quasi Newton Broyden-Fletcher-Goldfarb-Shanno
%       (L-BFGS)
%
% Classification is performed similarly to standard LVQ, with the only
% difference that for the computation of distances the above specified
% formula is applied.
%
% This function provides an interface in the style of the som-toolbox and
% is basically a wrapper for the method grlvq_core. It deals with structs
% rather then directly with data matrices.
%
% REFERENCES
%
%   Barbara Hammer, Thomas Villmann: Generalized relevance learning vector
%   quantization. Neural Networks 15(8-9): 1059-1068 (2002).
%
%   P. Schneider, K. Bunte, B. Hammer and M. Biehl: Regularization in
%   Matrix Relevance Learning, IEEE Transactions on Neural Networks, vol.
%   21, nb. 5, pp. 831-840, 2010.
%
% REQUIRED INPUT ARGUMENTS
%   sM                     (struct) map struct containing the initialized
%                                   prototype vectors as rows of the
%                                   .codebooks matrix and the labels as the
%                                   first column of .labels
%   sD                     (struct) data struct containing the data vectors
%                                   as rows of .data and the labels as the
%                                   first column of .labels
%
% OPTIONAL INPUT ARGUMENTS
%   PrototypesPerClass     (vector) number of prototypes per class used;
%                                   a number or a vector with a number
%                                   for each class (default=1)
%   initialRelevances      (vector) initial Relevances
%                                   (default: vector of ones)
%   regularization         (scalar) regularization for relevances
%                                   (detault=0)
%   comparable             (scalar) a flag which resets the random
%                                   generator to produce comparable results
%                                   if set to 1 (default=0)
%   optimization           (string) choice for the optimization technique:
%                                   sgd (stochastic gradient descent) or
%                                   fminlbfgs (Limited memory Quasi Newton
%                                   Broyden-Fletcher-Goldfarb-Shanno)
%                                   (default=fminlbfgs)
%  Parameter for the stochastic gradient descent sgd:
%   nb_epochs              (scalar) the number of epochs (default=100)
%   learningRatePrototypes (vector) learning rate for the prototypes; could
%                                   be the start and end value or a vector
%                                   of length nb_epochs
%   learningRateRelevances (vector) learning rate for the relevances; could
%                                   be the start and end value or a vector
%                                   of length nb_epochs
%   relevanceStart         (scalar) epoch to start the relevance learning
%                                   (default=1)
%  Parameter for the build-in function fminlbfgs:
%   threshstop             (scalar) the training error for early stopping
%                                   (default=0)
%   useEarlyStopping       (scalar) use early stopping based on threshstop
%                                   (default=1)
%   Display                (string) the optimization output 'iter' or 'off'
%                                   (default='off')
%   GradObj                (string) turn the usage of gradient information
%                                   on or off (default='on')
%   HessUpdate             (string) the update can be 'lbfgs', 'bfgs' or
%                                   'steepdesc' (default=lbfgs)
%   TolFun                 (scalar) the tolerance (default=1e-6)
%   MaxIter                (scalar) the maximal number of iterations
%                                   (default=2500)
%   MaxFunEvals                     the maximal number of function
%                                   evaluations (default=1000000)
%   TolX                   (scalar) tolerance on the minimum
%                                   (default=1e-10)
%   DiffMinChange          (scalar) minimal change (default=1e-10)
%
% OUTPUT ARGUMENTS
%   sM                     (struct) map struct containing the optimized
%                                   prototypes as well as the relevances
%                                   for each dimensionlambda
%   settings               (struct) information on the settings of the
%                                   algorithm
%
% EXAMPLES
%
%   lab   = unique(sD.labels(:,1));           % different classes
%   nProt = length(lab)*5;                    % 5 prototypes for each
%   sM = som_randinit(sD,'msize',[nProt 1]);  % initial prototypes
%   sM.labels = [lab;lab;lab;lab;lab];        % and their classes
%   sM = grlvq(sM,sD);                        % use GRLVQ to adjust the
%                                             % prototypes and the metric
%
% SEE ALSO
%
%   gmlvq         Use the GMLVQ algorithm for training.
%   grlvq_core    Access the GRLVQ functionality without using structs.
%   lvq3          Use LVQ3 algorithm for training.
%   lvq1          Use LVQ1 algorithm for training.

% Contributed to SOM Toolbox vs2, December 3rd, 2012 by Alexander Schulz
% Copyright (c) Alexander Schulz
% http://www.cis.hut.fi/projects/somtoolbox/

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

cods      = sCodebook.codebook;
codLabels = sCodebook.labels;
data      = sData.data;
labels    = sData.labels;

% convert labels from cell to a vector
labelsV    = som_label2num(labels);
codLabelsV = som_label2num(codLabels);

% calculate the number of prototypes per class
n_prot_per_class = zeros(1, max(codLabelsV));
for i=1:max(codLabelsV)
    n_prot_per_class(i) = sum(codLabelsV == i);
end

% call the main function
[model, settings]  = grlvq_core(data, labelsV, 'initialPrototypes', ...
    [cods, codLabelsV], 'PrototypesPerClass', n_prot_per_class, varargin{:});

% write the results in the output struct
sCodebook.codebook = model.w;
sCodebook.lambda   = model.lambda;

if strcmp(settings.optimization, 'sgd')
    trainlen   = settings.nb_epochs;
    alpha_ini  = settings.learningRatePrototypes(1);
    alpha_type = 'power';
else
    trainlen   = NaN;
    alpha_ini  = NaN;
    alpha_type = '';
end

sTrain = som_set('som_train','algorithm','GRLVQ',...
		 'data_name',sData.name,...
		 'neigh','',...
		 'mask',ones(size(model.w,2),1),...
		 'radius_ini',NaN,...
		 'radius_fin',NaN,...
		 'alpha_ini',alpha_ini,...
		 'alpha_type',alpha_type,...
		 'trainlen',trainlen,...
		 'time',datestr(now,0));
sCodebook.trainhist(end+1) = sTrain;


classdef ReasenbergDeclusterClass < ZmapFunction
    % Reasenberg Declustering codes
    % made from code originally from A.Allman that has chopped up and rebuilt

    properties
        taumin  duration    = days(1)   % look ahead time for not clustered events
        taumax  duration    = days(10)  % maximum look ahead time for clustered events
        P                   = 0.95      % confidence level that this is next event in sequence
        xk                  = 0.5       % is the factor used in xmeff

        % effective lower magnitude cutoff for catalog.
        % During clusteres, it is raised by a factor xk*cmag1
        xmeff               = 1.5

        rfact               = 10        % factor for interaction radius for dependent events
        err                 = 1.5       % epicenter error
        derr                = 2         % depth error, km
        %declustRoutine      = "ReasenbergDeclus"
        declusteredCatalog   ZmapCatalog
        replaceSequenceWithEquivMainshock   logical = false

         % if empty, clustering details will not be saved to workspace
        clusterDetailsVariableName          char    = 'cluster_details'

        % if empty, catalog will not be saved to workspace
        declusteredCatalogVariableName      char    = 'declustered_catalog'
        memorizeOriginalCatalog             logical = true
    end

    properties(Constant)
        PlotTag = "ReasenbergDecluster"

        ParameterableProperties = ["taumin", "taumax", "P",...
            "xk","xmeff","rfact","err","derr",..."declustRoutine",...
            "replaceSequenceWithEquivMainshock",...
            "clusterDetailsVariableName",...
            "declusteredCatalogVariableName",...
            "memorizeOriginalCatalog"];

        References = ['Paul Reasenberg (1985) ',...
            '"Second -order Moment of Central California Seismicity"',...
            ', JGR, Vol 90, P. 5479-5495.'];

        % interaction formula bundles assumptions about stress drop into a mag-dependent formula
        InteractFormula = struct('Reasenberg1985', @(m) 0.011 .* 10.^ (0.4 .* m),...
            'WellsCoppersmith1994', @(m) 0.01 * 10 .^ (0.5 * m))
    end

    methods
        function obj = ReasenbergDeclusterClass(catalog, varargin)
            % ReasenbergDeclusterClass

            obj@ZmapFunction(catalog);

            report_this_filefun();
            obj.parseParameters(varargin);
            obj.clusterDetailsVariableName = matlab.lang.makeValidName(obj.clusterDetailsVariableName);
            obj.declusteredCatalogVariableName = matlab.lang.makeValidName(obj.declusteredCatalogVariableName);
            obj.StartProcess();
        end

        function InteractiveSetup(obj)

            % make the interface

            zdlg = ZmapDialog();
            zdlg.AddHeader('Reasenberg Declustering parameters','FontSize',12);
            zdlg.AddHeader('look-ahead times');
            zdlg.AddDurationEdit('taumin', '(min) for UNclustered events' ,obj.taumin, 'TauMin look ahead time for not clustered events',@days);
            zdlg.AddDurationEdit('taumax', '(max) for   clustered events', obj.taumax,  'TauMax maximum look ahead time for clustered events',@days);
            zdlg.AddHeader('');
            zdlg.AddEdit('P',       'Confidence Level',             obj.P,          'P1 Confidence level : observing the next event in the sequence');
            zdlg.AddEdit('xk',      'XK factor',                    obj.xk,         'XK factor used in xmeff');
            zdlg.AddEdit('xmeff',   'Effective min mag cutoff',     obj.xmeff,   'XMEFF "effective" lower magnitude cutoff for catalog, during clusters, it is xmeff^{xk*cmag1}');
            zdlg.AddEdit('rfact',   'Interation radius factor:',    obj.rfact,      'RFACT>/b>factor for interaction radius for dependent events');
            zdlg.AddEdit('err',     'Epicenter error',              obj.err,        'Epicenter error');
            zdlg.AddEdit('derr',    'Depth error',                  obj.derr,       'derrDepth error');
            %zdlg.AddHeader('Output')
            zdlg.AddCheckbox('replaceSequenceWithEquivMainshock','Replace clusters with equivalent event',...
                obj.replaceSequenceWithEquivMainshock, {},...
                'Will replace each set of cluster earthquakes with a single event of equivalent Magnitude');
            zdlg.AddEdit('clusterDetailsVariableName',      'Save Clusters to workspace as', ...
                obj.clusterDetailsVariableName, 'if empty, then nothing will be separately saved');
            zdlg.AddEdit('declusteredCatalogVariableName',  'Save Declustered catalog to workspace as', ...
                obj.declusteredCatalogVariableName,'if empty, then nothing will be separately saved');
            zdlg.AddCheckbox('memorizeOriginalCatalog',     'Memorize Original catalog after sucessful decluster:', ...
                obj.memorizeOriginalCatalog, {}, 'Memorize original catalog prior to declustering');

            zdlg.Create('Name', 'Reasenberg Declustering','WriteToObj',obj,'OkFcn',@obj.Calculate);

        end

        function Results = Calculate(obj)
            calcFn = @obj.declus;
            [obj.declusteredCatalog, misc] = calcFn(obj);
            if nargout == 1
                Results = obj.declusteredCatalog;
            end

            obj.CalcFinishedFcn();
        end

        %{
        function [clustnum, eqs_in_clust] = decluster_from_python(obj)
              obj.verbose = config.get('verbose', obj.verbose);
            % Get relevant parameters
            neq = catalog.get_number_events()  % Number of earthquakes

            min_lookahead_days = obj.taumin;
            max_lookahead_days = obj.taumax;

            % Get elapsed days
            elapsed = days_from_first_event(catalog);

            assert(all(elapsed(2:end) >= elapsed(1:end-1)), "catalog needs to be in ascending date order")

            % easy-access variables
            dmethod='p2p';
            switch dmethod
                case 'gc'
                    surf_pos = [catalog.latitude, catalog.longitude];
                    event_distance = event_gc_distance;
                case'p2p'
                    surf_pos = geodetic_to_ecef(catalog.latitude, catalog.longitude);  % assumes at surface
                    event_distance = event_p2p_distance;
                otherwise
                    except(ValueError("unknown configuration dmethod. it should be 'gc' or 'p2p'"))
            end

            mags = catalog.magnitude;
            deps = catalog.depth;

            if isempty(obj.err)
                horiz_error = catalog.data.get('horizError', 0);
            else
                horiz_error = obj.err;
            end
            if isempty(obj.derr)
                depth_error = catalog.data.get('depthError', 0);
            else
                depth_error = obj.derr;
            end
            % Pre-allocate cluster index vectors
            vcl = zeros(neq, 1);

            % set the interaction zones, in km
            % Reasenberg 1987 or alternate version: Wells & Coppersmith 1994 / Helmstetter (SRL) 2007
            zone_noclust, zone_clust = obj.get_zone_distances_per_mag(mags, obj.rfact,...
                obj.interaction_formulas.(obj.interaction_formula), obj.taumax)

            k = 0  % clusterindex

            % variable to store information whether earthquake is already clustered
            clusmaxmag = ones(neq,1) * -inf;
            clus_biggest_idx = zeros(neq,1);

            % for every earthquake in catalog, main loop
            for i = 0 : neq-1 % in range(neq - 1)
                my_mag = mags(i);

                % variable needed for distance and timediff
                my_cluster = vcl(i);
                not_classified = my_cluster == 0;

                % attach interaction time

                if not_classified
                    obj.debug_print(i, ' is not in a cluster')
                    % this event is not associated with a cluster, yet
                    look_ahead_days = min_lookahead_days;

                elseif my_mag >= clusmaxmag(my_cluster)
                    % note, if this is now the biggest, then the cluster range collapses into its radius
                    printf('%d is the biggest event of cluster M=%g\n', i, my_mag);
                    % this is the biggest event  in this cluster, so far (or equal to it).
                    clusmaxmag(my_cluster) = my_mag;
                    clus_biggest_idx(my_cluster) = i;
                    look_ahead_days = min_lookahead_days;
                else
                    printf('%d is already in cluster, but not biggest', i);
                    % this event is already tied to a cluster, but is not the largest
                    idx_biggest = clus_biggest_idx(my_cluster);
                    days_since_biggest = elapsed(i) - elapsed(idx_biggest);
                    look_ahead_days = obj.clust_look_ahead_time(clusmaxmag(my_cluster),...
                        days_since_biggest, obj.xk, obj.xmeff, obj.P);

                    look_ahead_days(look_ahead_days<min_lookahead_days) = min_lookahead_days;
                    look_ahead_days(look_ahead_days>max_lookahead_days) = max_lookahead_days;
                end
                % extract eqs that fit interaction time window --------------

                max_elapsed = elapsed(i) + look_ahead_days;
                next_event = i + 1;
                last_event = bisect_left(elapsed, max_elapsed, next_event);
                temporal_evs = np.arange(next_event, last_event)
                if my_cluster ~= 0
                    temporal_evs = temporal_evs(vcl(temporal_evs) ~= my_cluster);
                end
                if len(temporal_evs) == 0
                    continue
                end
                % ------------------------------------
                % one or more events have now passed the time window test. Now compare
                % this subcatalog in space to A) most recent and B) largest event in cluster
                % ------------------------------------

                obj.debug_print('temporal_evs:', temporal_evs)
                my_biggest_idx = clus_biggest_idx(my_cluster)
                if not_classified
                    bg_ev_for_dist = i;
                else
                    bg_ev_for_dist = my_biggest_idx;
                end

                obj.debug_print('bg_ev_for_dist:', bg_ev_for_dist)
                % noinspection PyTypeChecker
                dist_to_recent = event_distance(surf_pos, deps, i, temporal_evs, horiz_error, depth_error)
                dist_to_biggest = event_distance(surf_pos, deps, bg_ev_for_dist, temporal_evs, horiz_error, depth_error)
                printf('dist_to_recent', dist_to_recent)
                printf('dist_to_biggest', dist_to_biggest)
                % extract eqs that fit the spatial interaction
                if look_ahead_days == min_lookahead_days
                    l_big = dist_to_biggest == 0;  % all false
                    l_recent = dist_to_recent <= zone_noclust(my_mag);
                    printf('Connecting those near to this event [dist <= {zone_noclust[my_mag]}]')
                else
                    l_big = dist_to_biggest <= zone_clust(clusmaxmag(my_cluster))
                    l_recent = dist_to_recent <= zone_clust(clusmaxmag(my_cluster))
                    printf('Connecting those near to this OR largest event [dist <= {zone_clust[clusmaxmag(my_cluster)]}]')
                end
                spatial_evs = l_recent | l_big;

                if ~any(spatial_evs)
                    continue
                end
                % ------------------------------------
                % one or more events have now passed both spatial and temporal window tests
                %
                % if there are events in this cluster that are already related to another
                % cluster, figure out the smallest cluster number. Then, assign all events
                % (both previously clustered and unclustered) to this new cluster number.
                % ------------------------------------

                % spatial events only include events AFTER i, not i itself
                % so vcl(events_in_any_cluster) is independent from vcl(i)

                candidates = temporal_evs(spatial_evs) ; % eqs that fit spatial and temporal criterion
                events_in_any_cluster = candidates(vcl(candidates) ~= 0);  % eqs which are already related with a cluster
                events_in_no_cluster = candidates(vcl(candidates) == 0);  % eqs that are not already in a cluster

                % if this cluster overlaps with any other cluster, then merge them
                % assign every event in all related clusters to the same (lowest) cluster number
                % set this cluster's maximum magnitude "clusmaxmag" to the largest magnitude of all combined events
                % set this cluster's clus_biggest_idx to the most recent largest event of all combined events

                if len(events_in_any_cluster) > 0
                    if not_classified
                        related_clust_nums = unique(vcl(events_in_any_cluster));
                    else
                        % include this cluster number in the reckoning
                        related_clust_nums = unique(np.hstack((vcl(events_in_any_cluster), my_cluster,)))
                    end
                    % associate all related events with my cluster
                    my_cluster = related_clust_nums(0);
                    vcl(i) = my_cluster;
                    vcl(candidates) = my_cluster;

                    for clustnum  = related_clust_nums
                        vcl(vcl == clustnum) = my_cluster;
                    end
                    events_in_my_cluster = vcl == my_cluster;
                    biggest_mag = np.max(mags(events_in_my_cluster));
                    biggest_mag_idx = find(mags == biggest_mag & events_in_my_cluster, 1, 'last');

                    % reset values for other clusters
                    clusmaxmag(related_clust_nums) = -inf;
                    clus_biggest_idx(related_clust_nums) = 0;

                    % assign values for this cluster
                    clusmaxmag(my_cluster) = biggest_mag;
                    clus_biggest_idx(my_cluster) = biggest_mag_idx;

                elseif my_cluster == 0
                    k = k + 1;
                    vcl(i) = k;
                    my_cluster = k;
                    clusmaxmag(my_cluster) = my_mag;
                    clus_biggest_idx(my_cluster) = i;
                else
                    pass  % no events found, and attached to existing cluster
                end
                % attach clustnumber to catalog yet unrelated to a cluster
                vcl(events_in_no_cluster) = my_cluster;

            end
            clustnum = vcl;
            eqs_in_clust = vcl > 0;
        end
        %}

        function [outputcatalog, details] = declus(obj, vals)
            % DECLUS main decluster algorithm
            % A.Allmann
            % main decluster algorithm
            % modified version, uses two different circles for already related events
            % works on catalog
            % different clusters stored with respective numbers in clus
            % Program is based on Raesenberg paper JGR;Vol90;Pages5479-5495;06/10/85
            %
            %basic variables used in the program
            %
            % interactzone_main_km  interaction zone for not clustered events
            % interactzone_in_clust_km     interaction zone for clustered events
            % rtest  radius in which the program looks for clusters
            % tau    look ahead time
            % tdiff  time difference between jth event and biggest eq
            % mbg    index of earthquake with biggest magnitude in a cluster
            % k      index of the cluster
            % k1     working index for cluster
            %
            % modified by Celso Reyes, 2017

            max_mag_in_cluster=[];
            idx_biggest_event_in_cluster=[];

            %% calculate interaction_zone  (1 value per event)

            interactzone_main_km = obj.InteractFormula.Reasenberg1985(obj.RawCatalog.Magnitude); %interaction zone for mainshock
            interactzone_in_clust_km = obj.rfact * interactzone_main_km;      %interaction zone if included in a cluster

            tau_min = days(obj.taumin);
            tau_max = days(obj.taumax);

            %calculation of the eq-time relative to 1902
            eqtime = days( obj.RawCatalog.Date - min(obj.RawCatalog.Date) );

            %variable to store information whether earthquake is already clustered
            clus = zeros(1,obj.RawCatalog.Count);

            k = 0;                                %clusterindex

            wai = waitbar(0,' Please Wait ...  Declustering the catalog');
            set(wai,'NumberTitle','off','Name','Declustering Progress');
            drawnow
            declustering_start = tic;
            %for every earthquake in catalog, main loop
            confine_value = @(value, min_val, max_val) max( min( value, max_val), min_val);
            for i = 1: (obj.RawCatalog.Count-1)

                % "myXXX" refers to the XXX for this event

                my_mag = obj.RawCatalog.Magnitude(i);

                if rem(i,50)==0
                    waitbar(i/(obj.RawCatalog.Count-1));
                end

                % variable needed for distance and timediff
                my_cluster = clus(i);
                alreadyInCluster = my_cluster~=0;
                not_classified = my_cluster==0;
                assert(not_classified~=alreadyInCluster)

                % attach interaction time
                if not_classified
                    look_ahead_days = tau_min;
                else
                    if my_mag >= max_mag_in_cluster(my_cluster)
                        max_mag_in_cluster(my_cluster) = my_mag;
                        idx_biggest_event_in_cluster(my_cluster) = i;
                        look_ahead_days = tau_min;
                    else
                        bgdiff = eqtime(i) - eqtime(idx_biggest_event_in_cluster(my_cluster));
                        look_ahead_days = clustLookAheadTime(obj.xk, max_mag_in_cluster(my_cluster), obj.xmeff, bgdiff, obj.P);
                        look_ahead_days = confine_value(look_ahead_days, tau_min, tau_max);
                    end
                end

                %extract eqs that fit interation time window
                temporal_evs = timediff(i, look_ahead_days, clus, eqtime); %local version

                if isempty(temporal_evs)
                    continue;
                end

                % ---------------------------
                % only continue if events passed the time test
                % ---------------------------

                rtest1 = interactzone_in_clust_km(i);
                if look_ahead_days == obj.taumin
                    rtest2 = 0;
                else
                    rtest2 = interactzone_main_km(idx_biggest_event_in_cluster(my_cluster));
                end

                if alreadyInCluster                % if i is already related with a cluster
                    tm1 = clus(temporal_evs) ~= my_cluster;  % eqs with a clustnumber different than i
                    if any(tm1)
                        temporal_evs = temporal_evs(tm1);
                    end
                    bg_ev_for_dist = idx_biggest_event_in_cluster(my_cluster);
                else
                    bg_ev_for_dist = i;
                end

                %calculate distances from the epicenter of biggest and most recent eq
                [dist1,dist2]=distance2(i,bg_ev_for_dist,temporal_evs, obj.RawCatalog);

                %extract eqs that fit the spatial interaction time
                sl0 = dist1<= rtest1 | dist2<= rtest2;

                if ~any(sl0)
                    continue
                end

                % ----------
                % only continue if events passed the distance test
                % ----------

                ll = temporal_evs(sl0);            %eqs that fit spatial and temporal criterion
                lla = ll(clus(ll)~=0);   %eqs which are already related with a cluster
                llb = ll(clus(ll)==0);   %eqs that are not already in a cluster
                if ~isempty(lla)         %find smallest clustnumber in the case several
                    sl1 = min(clus(lla));     %numbers are possible
                    if alreadyInCluster
                        my_cluster = min([sl1, my_cluster]);
                    else
                        my_cluster = sl1;
                    end
                    if clus(i)==0
                        clus(i) = my_cluster;
                    end
                    % merge related clusters together into cluster with the smallest number
                    sl2 = lla(clus(lla) ~= my_cluster);
                    if clus(i) ~= my_cluster
                        clus(clus==clus(i)) = my_cluster;
                    end

                    for j1 = sl2
                        if clus(j1) ~= my_cluster
                            clus(clus==clus(i)) = my_cluster;
                        end
                    end
                end

                if my_cluster==0   %if there was neither an event in the interaction zone nor i, already related to cluster
                    k = k+1;                         %
                    my_cluster = k;
                    clus(i) = my_cluster;
                    max_mag_in_cluster(my_cluster) = my_mag;
                    idx_biggest_event_in_cluster(my_cluster) = i;
                end

                if size(llb)>0     % attach clustnumber to events yet unrelated to a cluster
                    clus(llb) = my_cluster;  %
                end

            end

            close(wai);
            msg.dbfprintf('Declustering complete. It took %g seconds\n',toc(declustering_start));

            %% this table contains all we need to know about the clusters. maybe.
            details = table;
            details.Properties.UserData = struct;
            for j = 1 : numel(obj.ParameterableProperties)
                details.Properties.UserData.(obj.ParameterableProperties(j)) = obj.(obj.ParameterableProperties(j));
            end

            details.Properties.Description  = 'Details for cluster, from reasenberg declustering';
            details.eventNumber             = (1:obj.RawCatalog.Count)';
            details.clusterNumber           = clus(:);
            details.clusterNumber(details.clusterNumber==0) = missing;
            details.isBiggest               = false(size(details.clusterNumber));
            details.isBiggest(idx_biggest_event_in_cluster) = true;

            details.Latitude                = obj.RawCatalog.Y;
            details.Properties.VariableUnits(width(details)) = {'degrees'};

            details.Longitude               = obj.RawCatalog.X;
            details.Properties.VariableUnits(width(details)) = {'degrees'};

            details.Depth                   = obj.RawCatalog.Z;
            details.Properties.VariableUnits(width(details)) = {'kilometers'};

            details.Magnitude               = obj.RawCatalog.Magnitude;

            details.MagnitudeType           = obj.RawCatalog.MagnitudeType;

            details.Date                    = obj.RawCatalog.Date;

            details.InteractionZoneIfMain   = interactzone_main_km;
            details.Properties.VariableUnits(width(details)) = {'kilometers'};

            details.InteractionZoneIfInClust = interactzone_in_clust_km;
            details.Properties.VariableUnits(width(details)) = {'kilometers'};

            clusterFreeCatalog = obj.RawCatalog.subset(ismissing(details.clusterNumber));
            %biggest_events_in_cluster = obj.RawCatalog.subset(details.isBiggest);

            outputcatalog = clusterFreeCatalog;

            if ~any(clus)
                return
            end

            %build a matrix clust that stored clusters
            [~, biggest_events_in_cluster, max_mag_in_cluster,~,~] = funBuildclu(obj.RawCatalog,idx_biggest_event_in_cluster,clus,max_mag_in_cluster);

            % replace cluster sequences with summary events
            if obj.replaceSequenceWithEquivMainshock
                equi = obj.equevent(details(~ismissing(details.clusterNumber),:));  % calculates equivalent events
                if isempty(equi)
                    disp('No clusters in the catalog with this input parameters');
                    return;
                end
                tmpcat = cat(clusterFreeCatalog, equi);  %new, unsorted catalog

            else
                tmpcat = cat(clusterFreeCatalog, biggest_events_in_cluster); % builds catalog with biggest events instead
                disp('Original mainshocks kept');
            end

            tmpcat.sort('Date');

            tmpcat.Name = string(tmpcat.Name) + " (declust)";

            % save clustering details to workspace
            if ~isempty(obj.clusterDetailsVariableName)
                assignin('base',matlab.lang.makeValidName(obj.clusterDetailsVariableName),details);
            end

            if obj.memorizeOriginalCatalog
                mm = MemorizedCatalogManager();
                mm.memorize(obj.RawCatalog,'predeclust')
            end

            ZG              = ZmapGlobal.Data;
            ZG.original     = obj.RawCatalog;    %save catalog in variable original
            ZG.cluscat      = ZG.original.subset(clus(clus~=0));

            % save declustered catalog to workspace
            if ~isempty(obj.declusteredCatalogVariableName)
                assignin('base', obj.declusteredCatalogVariableName, tmpcat);
            end

            st1 = sprintf([' The declustering found %d clusters of earthquakes, a total of %d'...
                ' events (out of %d). The map window now displays the declustered catalog containing %d events.'], ...
                biggest_events_in_cluster.Count, ZG.cluscat.Count, ZG.original.Count , ZG.primeCatalog.Count);

            msgbox(st1,'Declustering Information')

            watchoff
            outputcatalog = tmpcat;

            obj.Result(1).values.cluster_details  = details;

        end

        function plot(obj, varargin)
            f = figure('Name','Reasenberg Deslustering Results');
            ax = subplot(2,2,1);
            ZG = ZmapGlobal.Data;
            biggest = obj.Result.values.cluster_details(obj.Result.values.cluster_details.isBiggest,:);
            non_cluster = obj.Result.values.cluster_details(ismissing(obj.Result.values.cluster_details.clusterNumber),:);
            msf = str2func(ZG.MainEventOpts.MarkerSizeFcn);
            scatter3(biggest.Longitude,biggest.Latitude,biggest.Depth,msf(biggest.Magnitude));
            ax.ZDir='reverse';
            title(ax,'Biggest events in each cluster');
            hold on
            ax.XLabel.String = 'Longitude';
            ax.YLabel.String = 'Latitude';
            ax.ZLabel.String = 'Depth [km]';
            feats = findobj(allchild(findobj('Tag','mainmap_ax')),'-regexp','Tag','mainmap_.+');
            copyobj(feats,ax); %copy features

            ax = subplot(2,2,2);

            ax = subplot(2,1,2);

            isInClust = ~ismissing(obj.Result.values.cluster_details.clusterNumber);
            isNotBig = ~obj.Result.values.cluster_details.isBiggest;
            clust = obj.Result.values.cluster_details(isInClust&isNotBig, :);
            scatter(clust.Date,clust.Depth,[],[.8 .8 .8],'Marker','.','DisplayName','other events in each cluster');
            ax.YDir='reverse';
            hold on;
            scatter(biggest.Date,biggest.Depth,msf(biggest.Magnitude),categorical(biggest.clusterNumber),'DisplayName','primary events in each cluster');
            cb = colorbar;
            cb.Label.String = 'Cluster #';
            ax.YLabel.String = 'Depth [km]';
            ax.XLabel.String = 'Date';
            title(ax,'Clusters through time');
            legend('show')
        end
    end

    methods(Static)
        function tau = clust_look_ahead_time(mag_big, dt_big, xk, xmeff, P)
            % CLUSTLOOKAHEAD calculate look ahead time for clustered events (days)
            deltam = (1-xk) .* mag_big - xmeff;
            if deltam < 0
                deltam = 0;
            end
            denom = 10.0 .^ ((deltam - 1) * (2/3));
            top = log(1 - P) * dt_big;
            tau = top / denom;
        end

        function h = AddMenuItem(parent, catalog, varargin)
            % create a menu item
            label = 'Reasenberg Decluster';
            h = uimenu(parent, 'Label', label,...
                'MenuSelectedFcn', @(~,~)ReasenbergDeclusterClass(catalog),...
                varargin{:});
        end

        function equi = equevent(tb)
            % equevent calc equivalent event to cluster
            % equi = equevent(catalog, cluster, bg)
            %   catalog : earthquake catalog
            %   cluster :
            %   bg : index of a big event (?)
            %  equevent.m                        A.Allmann
            % calculates equivalent event to a cluster
            % weight according to seismic moment
            % time for equivalent event is time of first biggest event
            %
            report_this_filefun();

            equi = ZmapCatalog;
            equi.Name='clusters';

            if isempty(tb)
                return
            end
            j = 0;
            nClusts = max(tb.clusterNumber);
            [elat, elon, edep, emag] = deal(nan(nClusts,1));
            edate(nClusts,1) = datetime(missing);
            emagtype(nClusts,1) = categorical(missing);

            for n = 1 : max(tb.clusterNumber)
                clust_events = tb(tb.clusterNumber==n,:);
                if isempty(clust_events)
                    continue;
                end
                j = j + 1;

                eqmoment = 10.^(clust_events.Magnitude .* 1.2);
                emoment = sum(eqmoment);         %moment

                weights = eqmoment./emoment;      %weightfactor
                elat(j)     = sum(clust_events.Latitude .* weights);
                elon(j)     = sum(clust_events.Longitude .* weights); %longitude
                edep(j)     = sum(clust_events.Depth .* weights); %depth
                emag(j)     = (log10(emoment))/1.2;
                theBiggest  = find(clust_events.isBiggest,1,'first');
                edate(j)    = clust_events.Date(theBiggest);
                emagtype(j) = clust_events.MagnitudeType(theBiggest);

            end

            %equivalent events for each cluster
            equi.Latitude = elat(1:j);
            equi.Longitude = elon(1:j);
            equi.Date = edate(1:j);
            equi.Magnitude = emag(1:j);
            equi.MagnitudeType = emagtype(1:j);
            equi.Depth = edep(1:j);
            [equi.Dip, equi.DipDirection, equi.Rake]=deal(nan(size(equi.Date)));
        end
    end

end

%% helper
function ac  = timediff(clus_idx, look_ahead_time, clus, eqtimes)
    % TIMEDIFF calculates the time difference between the ith and jth event
    % works with variable eqtime from function CLUSTIME
    % gives the indices ac of the eqs not already related to cluster k1
    % eqtimes should be sorted!
    %
    % clus_idx : ith cluster (ci)
    % look_ahead : look-ahead time (tau)
    % clus: clusters (length of catalog)
    % eqtimes: datetimes for event catalog, in days  [did not use duration because of overhead]
    %
    % tdiff: is time between jth event and eqtimes(clus_idx)
    % ac: index of each event within the cluster

    %assert(clus_idx <100, 'testing. remove me')

    comparetime = eqtimes(clus_idx);

    first_event = clus_idx + 1; % in cluster
    last_event = numel(eqtimes);
    max_elapsed = comparetime + look_ahead_time;

    if eqtimes(end) >= max_elapsed
        last_event = find(eqtimes(first_event : last_event) < max_elapsed, 1, 'last') + clus_idx;
    end

    if first_event == last_event
        % no additional events were found.
        ac = [];
        return
    end

    this_clusternum = clus(clus_idx);

    range = first_event : last_event;

    if this_clusternum == 0
        ac = range;
    else
        % indices of eqs not already related to this cluster
        ac = (find(clus(range) ~= this_clusternum)) + clus_idx;
    end
    ac = ac(:);
end


function a = lt( x, y )

%   Disciplined convex programming information for LT (<):
%      The left-hand side of a less-than constraint must be convex. The
%      right-hand side must be concave. Of course, real constant and
%      affine expressions are both convex and concave and can be used on
%      either side as well.
%
%   Disciplined geometric programming information for LT (<):
%      The left-hand side of a less-than constraint must be log-convex,
%      including positive constants, monomials, posynomials, generalized
%      posynomials, and products thereof. The right-hand side must be
%      log-concave---including positive constants, monomials,
%      reciprocals of log-convex expressions, and products thereof.
%
%   Note that CVX does not distinguish between strict less-than (<) and
%   less-than-or-equal (<=) constraints; they are treated identically.
%   Feasible interior-point solvers tend to return points which satisfy
%   strict inequality, but not all solvers do.

b = newcnstr( evalin( 'caller', 'cvx_problem', '[]' ), x, y, '<' );
if nargout, a = b; end

% Copyright 2005-2016 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.