function p = prior_sqrtt(varargin)
%PRIOR_SQRTT  Student-t prior structure for the square root of the parameter
%
%  Description
%    P = PRIOR_SQRTT('PARAM1', VALUE1, 'PARAM2', VALUE2, ...)
%    creates for the square root of the parameter Student's
%    t-distribution prior structure in which the named parameters
%    have the specified values. Any unspecified parameters are set
%    to default values.
%
%    P = PRIOR_SQRTT(P, 'PARAM1', VALUE1, 'PARAM2', VALUE2, ...)
%    modify a prior structure with the named parameters altered
%    with the specified values.
%
%    Parameters for Student-t prior [default]
%      mu       - location [0]
%      s2       - scale [1]
%      nu       - degrees of freedom [4]
%      mu_prior - prior for mu [prior_fixed]
%      s2_prior - prior for s2 [prior_fixed]
%      nu_prior - prior for nu [prior_fixed]
%
%  See also
%    PRIOR_t, PRIOR_*

% Copyright (c) 2000-2001,2010 Aki Vehtari
% Copyright (c) 2009 Jarno Vanhatalo
% Copyright (c) 2010 Jaakko Riihim�ki

% 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.

  ip=inputParser;
  ip.FunctionName = 'PRIOR_SQRTT';
  ip.addOptional('p', [], @isstruct);
  ip.addParamValue('mu',0, @(x) isscalar(x));
  ip.addParamValue('mu_prior',[], @(x) isstruct(x) || isempty(x));
  ip.addParamValue('s2',1, @(x) isscalar(x) && x>0);
  ip.addParamValue('s2_prior',[], @(x) isstruct(x) || isempty(x));
  ip.addParamValue('nu',4, @(x) isscalar(x) && x>0);
  ip.addParamValue('nu_prior',[], @(x) isstruct(x) || isempty(x));
  ip.parse(varargin{:});
  p=ip.Results.p;

  if isempty(p)
    init=true;
    p.type = 'Sqrt-t';
  else
    if ~isfield(p,'type') && ~isequal(p.type,'Sqrt-t')
      error('First argument does not seem to be a valid prior structure')
    end
    init=false;
  end

  % Initialize parameters
  if init || ~ismember('mu',ip.UsingDefaults)
    p.mu = ip.Results.mu;
  end
  if init || ~ismember('s2',ip.UsingDefaults)
    p.s2 = ip.Results.s2;
  end
  if init || ~ismember('nu',ip.UsingDefaults)
    p.nu = ip.Results.nu;
  end
  % Initialize prior structure
  if init
    p.p=[];
  end
  if init || ~ismember('mu_prior',ip.UsingDefaults)
    p.p.mu=ip.Results.mu_prior;
  end
  if init || ~ismember('s2_prior',ip.UsingDefaults)
    p.p.s2=ip.Results.s2_prior;
  end
  if init || ~ismember('nu_prior',ip.UsingDefaults)
    p.p.nu=ip.Results.nu_prior;
  end

  if init
    % set functions
    p.fh.pak = @prior_sqrtt_pak;
    p.fh.unpak = @prior_sqrtt_unpak;
    p.fh.lp = @prior_sqrtt_lp;
    p.fh.lpg = @prior_sqrtt_lpg;
    p.fh.recappend = @prior_sqrtt_recappend;
  end

end

function [w, s] = prior_sqrtt_pak(p)

  w=[];
  s={};
  if ~isempty(p.p.mu)
    w = p.mu;
    s=[s; 'Sqrt-Student-t.mu'];
  end
  if ~isempty(p.p.s2)
    w = [w log(p.s2)];
    s=[s; 'log(Sqrt-Student-t.s2)'];
  end
  if ~isempty(p.p.nu)
    w = [w log(p.nu)];
    s=[s; 'log(Sqrt-Student-t.nu)'];
  end
end

function [p, w] = prior_sqrtt_unpak(p, w)

  if ~isempty(p.p.mu)
    i1=1;
    p.mu = w(i1);
    w = w(i1+1:end);
  end
  if ~isempty(p.p.s2)
    i1=1;
    p.s2 = exp(w(i1));
    w = w(i1+1:end);
  end
  if ~isempty(p.p.nu)
    i1=1;
    p.nu = exp(w(i1));
    w = w(i1+1:end);
  end
end

function lp = prior_sqrtt_lp(x, p)

  lJ = -log(2*sqrt(x));  % log(1/(2*x^(1/2))) log(|J|) of transformation
  xt = sqrt(x);          % transformation
  lp = sum(gammaln((p.nu+1)./2) - gammaln(p.nu./2) - 0.5*log(p.nu.*pi.*p.s2) - (p.nu+1)./2.*log(1+(xt-p.mu).^2./p.nu./p.s2) + lJ);

  if ~isempty(p.p.mu)
    lp = lp + p.p.mu.fh.lp(p.mu, p.p.mu);
  end
  if ~isempty(p.p.s2)
    lp = lp + p.p.s2.fh.lp(p.s2, p.p.s2) + log(p.s2);
  end
  if ~isempty(p.p.nu)
    lp = lp + p.p.nu.fh.lp(p.nu, p.p.nu) + log(p.nu);
  end
end

function lpg = prior_sqrtt_lpg(x, p)

  lJg = -1./(2*x);        % gradient of log(|J|) of transformation
  xt  = sqrt(x);          % transformation
  xtg = 1./(2*sqrt(x));   % derivative of transformation
  lpg = xtg.*(-(p.nu+1).*(xt-p.mu)./(p.nu.*p.s2 + (xt-p.mu).^2)) + lJg;

  if ~isempty(p.p.mu)
    lpgmu = sum((p.nu+1).*(xt-p.mu)./(p.nu.*p.s2 + (xt-p.mu).^2)) + p.p.mu.fh.lpg(p.mu, p.p.mu);
    lpg = [lpg lpgmu];
  end
  if ~isempty(p.p.s2)
    lpgs2 = (sum(-1./(2.*p.s2)+((p.nu + 1)*(p.mu - xt)^2)./(2*p.s2*((p.mu-xt)^2 + p.nu*p.s2))) + p.p.s2.fh.lpg(p.s2, p.p.s2)).*p.s2 + 1;
    lpg = [lpg lpgs2];
  end
  if ~isempty(p.p.nu)
    lpgnu = (0.5*sum(digamma1((p.nu+1)./2)-digamma1(p.nu./2)-1./p.nu-log(1+(xt-p.mu).^2./p.nu./p.s2)+(p.nu+1)./(1+(xt-p.mu).^2./p.nu./p.s2).*(xt-p.mu).^2./p.s2./p.nu.^2) + p.p.nu.fh.lpg(p.nu, p.p.nu)).*p.nu + 1;
    lpg = [lpg lpgnu];
  end
end

function rec = prior_sqrtt_recappend(rec, ri, p)
% The parameters are not sampled in any case.
  rec = rec;
  if ~isempty(p.p.mu)
    rec.mu(ri,:) = p.mu;
  end
  if ~isempty(p.p.s2)
    rec.s2(ri,:) = p.s2;
  end
  if ~isempty(p.p.nu)
    rec.nu(ri,:) = p.nu;
  end
end


% *************************************************************************
% Video Super-Resolution with Convolutional Neural Networks
%
% helper function to generate mat file from video frames
% --> feel free to modify and use for your own purpose
%
% Version 1.0
%
% Created by:   Armin Kappeler
% Date:         02/19/2016
%
% *************************************************************************
clearvars

OUTPUTPATH = 'data/lr_video/u3/foreman_u4_LR.mat';

DParam.prescaling = 1;
DParam.upscaleFactor = 4;
DParam.patchSize = 36;
DParam.stride = 36;

inputBasename = {'foreman_short'};
inputNrFrames = 20;

inputVidBasePath = '/home/armin/caffe/data/Superresolution/4vid/';
inputVidPath{1} = [inputVidBasePath inputBasename{1} '/original/Frame '];
inputVidPath{2} = '.png';

for i=1:inputNrFrames
    fileIdxStr = sprintf('%03d',i);
    filename = [inputVidPath{1} fileIdxStr inputVidPath{2}];

    imgRGB = im2double(imread(filename));

    %% only get Y-channel (gray) from YCbCr
    if (ndims(imgRGB)==3)
        img_ycbcr = rgb2ycbcr(imgRGB);
        imgHi = img_ycbcr(:,:,1);
    else
        imgHi = imgRGB;
    end

    %% preprocess image
    imgHi = crop2divisibleSize(imgHi,DParam.upscaleFactor);
    imgLo = imresize(imgHi,1/DParam.upscaleFactor,'bicubic');

    if i==1
        frames = zeros([size(imgLo),inputNrFrames] );
    end

    frames(:,:,i) = imgLo;

end

save(OUTPUTPATH,'frames')


function prandtl_values_test ( )

%*****************************************************************************80
%
%% PRANDTL_VALUES_TEST demonstrates the use of PRANDTL_VALUES.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    03 November 2005
%
%  Author:
%
%    John Burkardt
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'PRANDTL_VALUES_TEST:\n' );
  fprintf ( 1, '  PRANDTL_VALUES stores values of\n' );
  fprintf ( 1, '  the Prandtl number of water\n' );
  fprintf ( 1, '  as a function of temperature and pressure.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '      T            P            Pr(T,P)\n' );
  fprintf ( 1, '\n' );

  n_data = 0;

  while ( 1 )

    [ n_data, tc, p, pr ] = prandtl_values ( n_data );

    if ( n_data == 0 )
      break
    end

    fprintf ( 1, '  %12f  %12f  %24.16f\n', tc, p, pr );

  end

  return
end


function [K, sK] = indexKernCompute(kern, x, x2)

% INDEXKERNCOMPUTE Compute the INDEX kernel given the parameters and X.
% FORMAT
% DESC computes the kernel parameters for the index based covariance function
% kernel given inputs associated with rows and columns.
% ARG kern : the kernel structure for which the matrix is computed.
% ARG x : the input matrix associated with the rows of the kernel.
% ARG x2 : the input matrix associated with the columns of the kernel.
% RETURN k : the kernel matrix computed at the given points.
%
% FORMAT
% DESC computes the kernel matrix for the index based covariance function
% kernel given a design matrix of inputs.
% ARG kern : the kernel structure for which the matrix is computed.
% ARG x : input data matrix in the form of a design matrix.
% RETURN k : the kernel matrix computed at the given points.
%
% SEEALSO : indexKernParamInit, kernCompute, kernCreate, indexKernDiagCompute, indexardKernCompute
%
% COPYRIGHT : Neil D. Lawrence, 2011

% KERN
  if size(x, 2)>1
    error('Index kernel requires 1-dimensional input.')
  end

  if nargin<3
    x2 = x;
  end
  sK = zeros(size(x, 1), size(x2, 1));
  for i = 1:size(x, 1)
    sK(i, :) = round(x(i))==round(x2)';
  end
  sK = sparse(sK);
  K = sK*kern.variance;
end


function [controlFlux, objFlux] = robustnessAnalysis(model, controlRxn, nPoints, plotResFlag, objRxn, objType)
% Performs robustness analysis for a reaction of
% interest and an objective of interest
%
% USAGE:
%
%    [controlFlux, objFlux] = robustnessAnalysis(model, controlRxn, nPoints, plotResFlag, objRxn, objType)
%
% INPUTS:
%    model:         COBRA model structure
%    controlRxn:    Reaction of interest whose value is to be controlled
%
% OPTIONAL INPUTS:
%    nPoints:        Number of points to show on plot (Default = 20)
%    plotResFlag:    Plot results (Default true)
%    objRxn:         Objective reaction to be maximized
%                    (Default = whatever is defined in model)
%    objType:        Maximize ('max') or minimize ('min') objective
%                    (Default = 'max')
%
% OUTPUTS:
%    controlFlux:    Flux values within the range of the maximum and minimum for
%                    reaction of interest
%    objFlux:        Optimal values of objective reaction at each control
%                    reaction flux value
%
% .. Author: - Monica Mo and Markus Herrgard 8/17/06

if (nargin < 3)
    nPoints = 20;
end
if (nargin < 4)
    plotResFlag = true;
end
if (nargin > 4)
    baseModel = changeObjective(model,objRxn);
else
    baseModel = model;
end
if (nargin <6)
    objType = 'max';
end

if (findRxnIDs(model,controlRxn))
    tmpModel = changeObjective(model,controlRxn);
    tmpModel.osenseStr = 'min';
    solMin = optimizeWBModel(tmpModel);
    tmpModel.osenseStr = 'max';
    solMax = optimizeWBModel(tmpModel);
else
    error('Control reaction does not exist!');
end

objFlux = [];
controlFlux = linspace(solMin.f,solMax.f,nPoints)';

showprogress(0,'Robustness analysis in progress ...');
for i=1:length(controlFlux)
    showprogress(i/length(controlFlux));
    modelControlled = changeRxnBounds(baseModel,controlRxn,controlFlux(i),'b');
    tmpModel.osenseStr = objType;
    solControlled = optimizeWBModel(modelControlled);
    objFlux(i) = solControlled.f;
end

objFlux = objFlux';

if (plotResFlag)
    plot(controlFlux,objFlux)
    xlabel(strrep(controlRxn,'_','-'));
    ylabel('Objective');
    axis tight;
end


function TessMat = in_tess_curry( TessFile )
% IN_TESS_CURRY: Read Curry tesselation files

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2012

% Open tesselation file
fid = fopen(TessFile, 'r');
if fid < 0
    error(['Cannot open file ', TessFile]);
end
% Initialize variables
TessMat = struct('Comment', '', 'Vertices', [], 'Faces', []);
curBlock = [];

% Read file line by line
while 1
    % Read one line
    newLine = fgetl(fid);
    if ~ischar(newLine)
        break;
    end
    % Lines to skip
    if isempty(newLine)
        continue;
    end
    % Identify blocks
    if ~isempty(strfind(newLine, 'LOCATION_LIST START_LIST'))
        curBlock = 'Vertices';
        continue;
    elseif ~isempty(strfind(newLine, 'TRIANGLE_LIST START_LIST'))
        curBlock = 'Faces';
        continue;
    elseif ~isempty(strfind(newLine, 'POINT_DESCRIPTION START_LIST'))
        curBlock = 'Comment';
        continue;
    elseif ~isempty(strfind(newLine, 'END'))
        curBlock = [];
        continue;
    end
    % If no block is open: skip the line
    if isempty(curBlock)
        continue;
    end
    % Numeric values
    if ismember(curBlock, {'Vertices', 'Faces'})
        % Interpret values: 3 values per line
        values = str2num(newLine);
        if (length(values) < 3)
            continue;
        end
        % Add to the list
        TessMat.(curBlock) = [TessMat.(curBlock); values];
    % Text
    else
        TessMat.(curBlock) = [TessMat.(curBlock), newLine];
        % Read only the first line of the comments
        if strcmpi(curBlock, 'Comment')
            curBlock = [];
        end
    end
end
% Close file
fclose(fid);

% Check that some vertices where loaded
if isempty(TessMat.Vertices)
    error('This file does not contain any Curry BEM surface.');
end
% Rebuild tesselation
if isempty(TessMat.Faces)
    % Get the convex envelope of the points
    TessMat.Faces = convhulln(TessMat.Vertices);
else
    % Convert from 0-based to 1-based indices
    TessMat.Faces = TessMat.Faces + 1;
    % Reverse faces order
    TessMat.Faces = TessMat.Faces(:, [1 3 2]);
end


function [mergePsi, logQ] = getMergeConfig_SeqAllocation( Psi, data_struct, hyperparams, model, anchorObjIDs, featIDs, TargetPsi )
% Merge Move launch
%   MERGE all blocks assigned to either featIDs into single feature kmerge
if ~exist( 'TargetPsi', 'var' )
    TargetPsi = [];
end

if isfield( Psi, 'theta' );
    Psi = rmfield( Psi, 'theta');
    Psi = rmfield( Psi, 'TS');
end

logQ.F   = 0;
logQ.z   = 0;
obsModel = model.obsModel;

F = Psi.F;
nObj = size(F,1);
ii = anchorObjIDs(1);
jj = anchorObjIDs(2);

allObjIDs = find( sum( F(:,featIDs), 2 ) > 0 )';
otherObjIDs = setdiff( allObjIDs, anchorObjIDs );
otherFeatIDs = setdiff( 1:size(F,2), featIDs );

propF = F;
propF(:, featIDs) = 0;
kmerge = size(propF,2)+1;

propFeatIDs = [kmerge];
propF(allObjIDs, kmerge) = 1;

propStateSeq = Psi.stateSeq;
for aa = anchorObjIDs
    aIDs =  propStateSeq(aa).z == featIDs(1);
    bIDs =  propStateSeq(aa).z == featIDs(2);
    propStateSeq(aa).z( aIDs | bIDs ) = kmerge;
end
propTS = getEta_PriorMean( propF, [], hyperparams, model, allObjIDs, propFeatIDs );

% Ustats  = getStateSeqSuffStats_C1( propStateSeq, propF, data_struct, model, 1:nObj, [otherFeatIDs propFeatIDs] );
% myTheta.logp = zeros( size(Ustats.Nkv)  );
% myTheta = getTheta_PosteriorMean( myTheta, Ustats, obsModel, [otherFeatIDs propFeatIDs] );

[Xstats] = getXSuffStats( propF, propStateSeq, data_struct, model, 1:nObj, [otherFeatIDs propFeatIDs] );
myTheta = setThetaToPosteriorMean( [], Xstats, obsModel, [otherFeatIDs propFeatIDs] );

% Keep *only* the params for the feature kM being merged
%   this is all we update
AXstats  = getXSuffStats( propF, propStateSeq, data_struct, model, [ii jj], [propFeatIDs] );

if exist( 'TargetPsi', 'var' ) && ~isempty( TargetPsi )
    TargetPsi.externalFeatIDs = kmerge;
end
for aa = [ otherObjIDs( randperm( length(otherObjIDs) ) ) anchorObjIDs]
    if aa == ii || aa == jj
        Xdelta = getXSuffStats( propF, propStateSeq, data_struct, model, aa, propFeatIDs );
        AXstats = decXStats( AXstats, Xdelta, model.obsModel );
    end

    % Construct soft evidence
    activeFeatIDs = find( propF(aa,:)==1 );
    LL = calcLogCondPrObsGivenTheta( data_struct(aa), myTheta, obsModel, activeFeatIDs, size(propF,2) );
    LL = LL( activeFeatIDs, :);
    % Propose new State Seq. for current item
    [propStateSeq(aa).z, logPrQ_z] = sampleStateSeq_PrecompSoftEv( aa, propTS.obj(aa), LL, TargetPsi);
    %[propStateSeq, logQaa] = sampleZ_RGS(data_struct, propTS, propF, myTheta, model.obsModel, propStateSeq, aa, TargetPsi);
    logQ.z = logQ.z + logPrQ_z;

    % Update Emission Params for new proposed features
    %   using the newly allocated data from the current sequence
    Xdelta = getXSuffStats( propF, propStateSeq, data_struct, model, aa, propFeatIDs );
    AXstats = incXStats( AXstats, Xdelta, model.obsModel );
    myTheta = setThetaToPosteriorMean( myTheta, AXstats, obsModel, propFeatIDs );
end

mergePsi.F = propF;
mergePsi.stateSeq = propStateSeq;
mergePsi.activeFeatIDs = propFeatIDs;
mergePsi.anchorObjIDs  = anchorObjIDs;


function exact = p06_exact ( )

%*****************************************************************************80
%
%% P06_EXACT returns the exact integral for problem 6.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    04 November 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, real EXACT, the estimated value of the integral.
%
  exact = 1.460447131787105;

  return
end


ndataset = {};
location = ['rooms',filesep];
for i = 1:length(dataset)
    data = dataset{i};
    obblist = data.obblist;
    nobblist = [];
    for j = 1:size(obblist,2)
        obb = obblist(1:12,j);
        label = obblist(13:end,j);
        label = binary2dec(label);
        cornerpoints = OBBrep2cornerpoints(obb);
        nobb = [label];
        for k = 1:size(cornerpoints,1)
            nobb = [nobb;cornerpoints(k,:)'];
        end
        nobblist = [nobblist,nobb];
    end
    ndataset{i} = nobblist;

    filename = [location, num2str(i),'.txt'];
    fid = fopen(filename,'w');
    for j = 1:size(nobblist,2)
        nobb = nobblist(:,j);
        for k = 1:length(nobb)
            fprintf(fid, '%f ', nobb(k));
        end
        fprintf(fid,'\n');
    end
    fclose(fid);
end


function [CV]=faceToVertexMeasure(F,V,CF)
% function [CV]=faceToVertexMeasure(F,V,CF)
% ------------------------------------------------------------------------
%
%
% Change log:
% 2009
% 2019/04/22 Minor changes to improve efficiency through improved allocation
% 2019/04/22 Renamed variables
% 2019/04/23 Updated to handle cell input
% ------------------------------------------------------------------------

%%

%Check if F is a cell of face arrays
cellMode=isa(F,'cell');

if cellMode==1
    CV=repmat({zeros(size(V,1),size(CF{1},2))},1,numel(CF));
    for qc=1:1:numel(F)
        % Get vertex-face connectivity matrix
        conStruct=patchConnectivity(F{qc},V,'vf');
        IND_F=conStruct.vertex.face;
        logicValid=IND_F>0;
        CV_now=ones(size(V,1),size(IND_F,2),size(CF{qc},2));
        cv=nan(size(IND_F));
        for q=1:1:size(CF{qc},2) %Loop over data columns
            cf=CF{qc}(:,q);
            cv(logicValid)=cf(IND_F(logicValid));
            CV_now(:,:,q)=cv;
        end
        CV{qc}=CV_now;
    end
    CV=mean(cell2mat(CV),2,'omitnan'); %Take mean across faces
    if size(CF{1},2)>1
        CV=permute(CV,[1 3 2]); %Get rid of singleton dimension
    end
else
    % Get vertex-face connectivity matrix
    conStruct=patchConnectivity(F,V,'vf');
    IND_F=conStruct.vertex.face;
    logicValid=IND_F>0;
    CV=ones(size(V,1),size(CF,2));
    cv=nan(size(IND_F));
    for q=1:1:size(CF,2) %Loop over data columns
        cf=CF(:,q);
        cv(logicValid)=cf(IND_F(logicValid));
        CV(:,q)=mean(cv,2,'omitnan');
    end
end

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function test_bug3337

% WALLTIME 00:10:00
% MEM 1gb
% DEPENDENCY

%%
data = [];
data.label = {'1', '2'};
for i=1:3
  data.trial{i} = randn(2, 1000);
  data.time{i} = (1:1000)/1000;
  data.sampleinfo(i,:) = [(i-1)*1000+1 i*1000];
end

data.trial{1}(:,30:40)  = nan;
data.trial{1}(:,80:90)  = nan; % 2nd segment within one trial
data.trial{2}(1,100)    = nan; % one sample
data.trial{3}(1,:)      = nan; % whole trial

cfg = [];
[cfg, artifact] = ft_artifact_nan(cfg, data);

assert(size(artifact,1)==4)
assert(size(artifact,2)==2)

correct = [
    30          40
    80          90
  1100        1100
  2001        3000
  ];

assert(isequal(artifact, correct));

%%

cfg.artfctdef.reject = 'nan';
datanan = ft_rejectartifact(cfg, data);
assert(isequaln(data.trial{1}, datanan.trial{1}));

%%

cfg.artfctdef.minaccepttim = 0;
cfg.artfctdef.reject = 'partial';
datapartial = ft_rejectartifact(cfg, data);
assert(numel(datapartial.trial)==5);


function [x, f, eflag, output, lambda] = qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt)
%QPS_GUROBI  Quadratic Program Solver based on GUROBI.
%   [X, F, EXITFLAG, OUTPUT, LAMBDA] = ...
%       QPS_GUROBI(H, C, A, L, U, XMIN, XMAX, X0, OPT)
%   [X, F, EXITFLAG, OUTPUT, LAMBDA] = QPS_GUROBI(PROBLEM)
%   A wrapper function providing a standardized interface for using
%   GUROBI to solve the following QP (quadratic programming)
%   problem:
%
%       min 1/2 X'*H*X + C'*X
%        X
%
%   subject to
%
%       L <= A*X <= U       (linear constraints)
%       XMIN <= X <= XMAX   (variable bounds)
%
%   Inputs (all optional except H, C, A and L):
%       H : matrix (possibly sparse) of quadratic cost coefficients
%       C : vector of linear cost coefficients
%       A, L, U : define the optional linear constraints. Default
%           values for the elements of L and U are -Inf and Inf,
%           respectively.
%       XMIN, XMAX : optional lower and upper bounds on the
%           X variables, defaults are -Inf and Inf, respectively.
%       X0 : optional starting value of optimization vector X
%       OPT : optional options structure with the following fields,
%           all of which are also optional (default values shown in
%           parentheses)
%           verbose (0) - controls level of progress output displayed
%               0 = no progress output
%               1 = some progress output
%               2 = verbose progress output
%               3 = even more verbose progress output
%           grb_opt - options struct for GUROBI, value in verbose
%                   overrides these options
%       PROBLEM : The inputs can alternatively be supplied in a single
%           PROBLEM struct with fields corresponding to the input arguments
%           described above: H, c, A, l, u, xmin, xmax, x0, opt
%
%   Outputs:
%       X : solution vector
%       F : final objective function value
%       EXITFLAG : GUROBI exit flag
%           1 = converged
%           0 or negative values = negative of GUROBI exit flag
%           (see GUROBI documentation for details)
%       OUTPUT : GUROBI output struct
%           (see GUROBI documentation for details)
%       LAMBDA : struct containing the Langrange and Kuhn-Tucker
%           multipliers on the constraints, with fields:
%           mu_l - lower (left-hand) limit on linear constraints
%           mu_u - upper (right-hand) limit on linear constraints
%           lower - lower bound on optimization variables
%           upper - upper bound on optimization variables
%
%   Note the calling syntax is almost identical to that of QUADPROG
%   from MathWorks' Optimization Toolbox. The main difference is that
%   the linear constraints are specified with A, L, U instead of
%   A, B, Aeq, Beq.
%
%   Calling syntax options:
%       [x, f, exitflag, output, lambda] = ...
%           qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt)
%
%       x = qps_gurobi(H, c, A, l, u)
%       x = qps_gurobi(H, c, A, l, u, xmin, xmax)
%       x = qps_gurobi(H, c, A, l, u, xmin, xmax, x0)
%       x = qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt)
%       x = qps_gurobi(problem), where problem is a struct with fields:
%                       H, c, A, l, u, xmin, xmax, x0, opt
%                       all fields except 'c', 'A' and 'l' or 'u' are optional
%       x = qps_gurobi(...)
%       [x, f] = qps_gurobi(...)
%       [x, f, exitflag] = qps_gurobi(...)
%       [x, f, exitflag, output] = qps_gurobi(...)
%       [x, f, exitflag, output, lambda] = qps_gurobi(...)
%
%
%   Example: (problem from from https://v8doc.sas.com/sashtml/iml/chap8/sect12.htm)
%       H = [   1003.1  4.3     6.3     5.9;
%               4.3     2.2     2.1     3.9;
%               6.3     2.1     3.5     4.8;
%               5.9     3.9     4.8     10  ];
%       c = zeros(4,1);
%       A = [   1       1       1       1;
%               0.17    0.11    0.10    0.18    ];
%       l = [1; 0.10];
%       u = [1; Inf];
%       xmin = zeros(4,1);
%       x0 = [1; 0; 0; 1];
%       opt = struct('verbose', 2);
%       [x, f, s, out, lambda] = qps_gurobi(H, c, A, l, u, xmin, [], x0, opt);
%
%   See also QPS_MASTER, GUROBI.

%   MP-Opt-Model
%   Copyright (c) 2010-2020, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MP-Opt-Model.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://github.com/MATPOWER/mp-opt-model for more info.

%%----- input argument handling  -----
%% gather inputs
if nargin == 1 && isstruct(H)       %% problem struct
    p = H;
    if isfield(p, 'opt'),   opt = p.opt;    else,   opt = [];   end
    if isfield(p, 'x0'),    x0 = p.x0;      else,   x0 = [];    end
    if isfield(p, 'xmax'),  xmax = p.xmax;  else,   xmax = [];  end
    if isfield(p, 'xmin'),  xmin = p.xmin;  else,   xmin = [];  end
    if isfield(p, 'u'),     u = p.u;        else,   u = [];     end
    if isfield(p, 'l'),     l = p.l;        else,   l = [];     end
    if isfield(p, 'A'),     A = p.A;        else,   A = [];     end
    if isfield(p, 'c'),     c = p.c;        else,   c = [];     end
    if isfield(p, 'H'),     H = p.H;        else,   H = [];     end
else                                %% individual args
    if nargin < 9
        opt = [];
        if nargin < 8
            x0 = [];
            if nargin < 7
                xmax = [];
                if nargin < 6
                    xmin = [];
                end
            end
        end
    end
end

%% define nx, set default values for missing optional inputs
if isempty(H) || ~any(any(H))
    if isempty(A) && isempty(xmin) && isempty(xmax)
        error('qps_gurobi: LP problem must include constraints or variable bounds');
    else
        if ~isempty(A)
            nx = size(A, 2);
        elseif ~isempty(xmin)
            nx = length(xmin);
        else    % if ~isempty(xmax)
            nx = length(xmax);
        end
    end
else
    nx = size(H, 1);
end
if isempty(c)
    c = zeros(nx, 1);
end
if isempty(A) || (~isempty(A) && (isempty(l) || all(l == -Inf)) && ...
                                 (isempty(u) || all(u == Inf)))
    A = sparse(0,nx);           %% no limits => no linear constraints
end
nA = size(A, 1);                %% number of original linear constraints
if isempty(u)                   %% By default, linear inequalities are ...
    u = Inf(nA, 1);             %% ... unbounded above and ...
end
if isempty(l)
    l = -Inf(nA, 1);            %% ... unbounded below.
end
if isempty(xmin)                %% By default, optimization variables are ...
    xmin = -Inf(nx, 1);         %% ... unbounded below and ...
end
if isempty(xmax)
    xmax = Inf(nx, 1);          %% ... unbounded above.
end
if isempty(x0)
    x0 = zeros(nx, 1);
end

%% default options
if ~isempty(opt) && isfield(opt, 'verbose') && ~isempty(opt.verbose)
    verbose = opt.verbose;
else
    verbose = 0;
end

%% set up options struct for Gurobi
if ~isempty(opt) && isfield(opt, 'grb_opt') && ~isempty(opt.grb_opt)
    g_opt = gurobi_options(opt.grb_opt);
else
    g_opt = gurobi_options;
end
if verbose > 1
    g_opt.LogToConsole = 1;
    g_opt.OutputFlag = 1;
    if verbose > 2
        g_opt.DisplayInterval = 1;
    else
        g_opt.DisplayInterval = 100;
    end
else
    g_opt.LogToConsole = 0;
    g_opt.OutputFlag = 0;
end

if ~issparse(A)
    A = sparse(A);
end
if issparse(c);
    c = full(c);
end

%% split up linear constraints
ieq = find( abs(u-l) <= eps );          %% equality
igt = find( u >=  1e10 & l > -1e10 );   %% greater than, unbounded above
ilt = find( l <= -1e10 & u <  1e10 );   %% less than, unbounded below
ibx = find( (abs(u-l) > eps) & (u < 1e10) & (l > -1e10) );

%% grab some dimensions
nlt = length(ilt);      %% number of upper bounded linear inequalities
ngt = length(igt);      %% number of lower bounded linear inequalities
nbx = length(ibx);      %% number of doubly bounded linear inequalities
neq = length(ieq);      %% number of equalities
niq = nlt+ngt+2*nbx;    %% number of inequalities

%% set up model
m.A     = [ A(ieq, :); A(ilt, :); -A(igt, :); A(ibx, :); -A(ibx, :) ];
m.rhs   = [ u(ieq);    u(ilt);    -l(igt);    u(ibx);    -l(ibx)    ];
m.sense = char([ double('=')*ones(1,neq) double('<')*ones(1,niq) ]);
m.lb = xmin;
m.ub = xmax;
m.obj = c';

%% call the solver
if isempty(H) || ~any(any(H))
    lpqp = 'LP';
else
    lpqp = 'QP';
    if ~issparse(H)
        H = sparse(H);
    end
    m.Q = 0.5 * H;
end
if verbose
    alg_names = {
        'automatic',
        'primal simplex',
        'dual simplex',
        'interior point',
        'concurrent',
        'deterministic concurrent',
        'deterministic concurrent simplex'
    };
    vn = gurobiver;
    fprintf('Gurobi Version %s -- %s %s solver\n', ...
        vn, alg_names{g_opt.Method+2}, lpqp);
end
results = gurobi(m, g_opt);
switch results.status
    case 'LOADED',          %% 1
        eflag = -1;
    case 'OPTIMAL',         %% 2, optimal solution found
        eflag = 1;
    case 'INFEASIBLE',      %% 3
        eflag = -3;
    case 'INF_OR_UNBD',     %% 4
        eflag = -4;
    case 'UNBOUNDED',       %% 5
        eflag = -5;
    case 'CUTOFF',          %% 6
        eflag = -6;
    case 'ITERATION_LIMIT', %% 7
        eflag = -7;
    case 'NODE_LIMIT',      %% 8
        eflag = -8;
    case 'TIME_LIMIT',      %% 9
        eflag = -9;
    case 'SOLUTION_LIMIT',  %% 10
        eflag = -10;
    case 'INTERRUPTED',     %% 11
        eflag = -11;
    case 'NUMERIC',         %% 12
        eflag = -12;
    case 'SUBOPTIMAL',      %% 13
        eflag = -13;
    case 'INPROGRESS',      %% 14
        eflag = -14;
    otherwise,
        eflag = 0;
end
output = results;

%% check for empty results (in case optimization failed)
if ~isfield(results, 'x') || isempty(results.x)
    x = NaN(nx, 1);
    lam.lower   = NaN(nx, 1);
    lam.upper   = NaN(nx, 1);
else
    x = results.x;
    lam.lower   = zeros(nx, 1);
    lam.upper   = zeros(nx, 1);
end
if ~isfield(results, 'objval') || isempty(results.objval)
    f = NaN;
else
    f = results.objval;
end
if ~isfield(results, 'pi') || isempty(results.pi)
    pi  = NaN(length(m.rhs), 1);
else
    pi  = results.pi;
end
if ~isfield(results, 'rc') || isempty(results.rc)
    rc  = NaN(nx, 1);
else
    rc  = results.rc;
end

kl = find(rc > 0);   %% lower bound binding
ku = find(rc < 0);   %% upper bound binding
lam.lower(kl)   =  rc(kl);
lam.upper(ku)   = -rc(ku);
lam.eqlin   = pi(1:neq);
lam.ineqlin = pi(neq+(1:niq));
mu_l        = zeros(nA, 1);
mu_u        = zeros(nA, 1);

%% repackage lambdas
kl = find(lam.eqlin > 0);   %% lower bound binding
ku = find(lam.eqlin < 0);   %% upper bound binding

mu_l(ieq(kl)) = lam.eqlin(kl);
mu_l(igt) = -lam.ineqlin(nlt+(1:ngt));
mu_l(ibx) = -lam.ineqlin(nlt+ngt+nbx+(1:nbx));

mu_u(ieq(ku)) = -lam.eqlin(ku);
mu_u(ilt) = -lam.ineqlin(1:nlt);
mu_u(ibx) = -lam.ineqlin(nlt+ngt+(1:nbx));

lambda = struct( ...
    'mu_l', mu_l, ...
    'mu_u', mu_u, ...
    'lower', lam.lower, ...
    'upper', lam.upper ...
);


%% t_meshShowContrast
%
% Illustrates how to project a contrast map onto a 3D mesh
%
% Before proceeding with tutorial:
%	* Run t_initGrayAndVolume.
%	* Run t_glmRun.
%	* Run t_glmComputeContrastMap.
%	* Run t_convertMapInplaneToGray
%
% See also T_INITGRAYANDVOLUME, T_GLMRUN, T_GLMCOMPUTECONTRASTMAP, T_CONVERTMAPINPLANETOGRAY.
%
% JW (c) Stanford VISTA
%

%% Initialize the key variables and data path:
dataDir     = fullfile(mrvDataRootPath,'functional','vwfaLoc');
pathToMesh  = fullfile(mrvDataRootPath, 'anatomy','anatomyNIFTI', 'leftMesh.mat');
pathToMap   = fullfile(dataDir, 'Gray', 'GLMs', 'FixVWordScrambleWord.mat');
displayMode = 'co'; % Co-thresholded display mode
threshold   = .05; % Co-threshold

%% Retain original directory, change to data directory
curDir = pwd;
cd(dataDir);

%% Load data structure, parameter map, and 3D mesh
vw_gray = initHiddenGray();
vw_gray = loadParameterMap(vw_gray, pathToMap);
vw_gray = meshLoad(vw_gray, pathToMesh, 1);

%% Select display mode and threshold
vw_gray = viewSet(vw_gray, 'displaymode', 'co');
vw_gray = viewSet(vw_gray, 'cothresh', threshold);

%% Set bicolor colormap (neg and pos values)
vw_gray = bicolorCmap(vw_gray);

%% Overlay contrast map onto mesh
vw_gray = meshColorOverlay(vw_gray);

%% Restore original directory
cd(curDir);

%% END


function grad = B_beamforming(future_layers, input_layers, curr_layer)
weight = input_layers{1}.a;
nBin = length(curr_layer.freqBin);
input = input_layers{2}.a;

[Di,Ti,Ni] = size(input);
[Dw,Tw,Nw] = size(weight);

future_grad = GetFutureGrad(future_layers, curr_layer);
future_grad = permute(future_grad, [1 2 4 3]);
input = reshape(input, nBin,Di/nBin,Ti,Ni);

input2 = permute(input, [1 3 2 4]);
grad{1} = bsxfun(@times, conj(input2), future_grad);
grad{1} = permute(grad{1}, [1 3 2 4]);
grad{1} = reshape(grad{1}, Di,Ti,Ni);

if Tw==1
    grad{1} = sum(grad{1},2);
end

grad{2} = [];

end


function x = p16_start ( option, nvar )

%*****************************************************************************80
%
%% P16_START returns a starting point for problem 16.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    03 September 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer OPTION, the option index.
%
%    Input, integer NVAR, the number of variables.
%
%    Output, real X(NVAR), the starting point.
%
  x = zeros ( nvar, 1 );

  x(1:nvar-1) = 1.0;
  x(nvar) = 0.0;

  return
end


function [gist, param] = LMgist(D, HOMEIMAGES, param, HOMEGIST)
%
% param.imageSize = [256 256]; % it works also with non-square images (use the most common aspect ratio in your set)
% param.orientationsPerScale = [8 8 8 8]; % number of orientations per scale
% param.numberBlocks = 4;
% param.fc_prefilt = 4;
%
% [gist, param] = LMgist(D, HOMEIMAGES, param);
% [gist, param] = LMgist(filename, HOMEIMAGES, param);
% [gist, param] = LMgist(filename, HOMEIMAGES, param, HOMEGIST);
%
% For a one image or several images of the same size in a 4D array, use:
% gist = LMgist(img, [], param);
%
% When calling LMgist with a fourth argument it will store the gists in a
% new folder structure mirroring the folder structure of the images. Then,
% when called again, if the gist files already exist, it will just read
% them without recomputing them:
%
%   [gist, param] = LMgist(filename, HOMEIMAGES, param, HOMEGIST);
%   [gist, param] = LMgist(D, HOMEIMAGES, param, HOMEGIST);
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Modeling the shape of the scene: a holistic representation of the spatial envelope
% Aude Oliva, Antonio Torralba
% International Journal of Computer Vision, Vol. 42(3): 145-175, 2001.

if nargin==4
    precomputed = 1;
    % get list of folders and create non-existing ones
    %listoffolders = {D(:).annotation.folder};

    %for i = 1:length(D);
    %    f{i} = D(i).annotation.folder;
    %end
    %[categories,b,class] = unique(f);
else
    precomputed = 0;
    HOMEGIST = '';
end

param.boundaryExtension = 32; % number of pixels to pad

if nargin<3
    % Default parameters
    param.imageSize = 128;
    param.orientationsPerScale = [8 8 8 8];
    param.numberBlocks = 4;
    param.fc_prefilt = 4;
    param.G = createGabor(param.orientationsPerScale, param.imageSize(1)+2*param.boundaryExtension);
else
    if ~isfield(param, 'G')
        param.G = createGabor(param.orientationsPerScale, param.imageSize(1)+2*param.boundaryExtension);
    end
end

% Precompute filter transfert functions (only need to do this once, unless
% image size is changes):
Nfeatures = size(param.G,3)*param.numberBlocks^2;

if isstruct(D)
    % [gist, param] = LMgist(D, HOMEIMAGES, param);
    Nscenes = length(D);
    typeD = 1;
end
if iscell(D)
    % [gist, param] = LMgist(filename, HOMEIMAGES, param);
    Nscenes = length(D);
    typeD = 2;
end
if isnumeric(D)
    % [gist, param] = LMgist(img, HOMEIMAGES, param);
    Nscenes = size(D,4);
    typeD = 3;
end

% Loop: Compute gist features for all scenes
gist = zeros([Nscenes Nfeatures], 'single');
for n = 1:Nscenes
    g = [];
    todo = 1;

    % if gist has already been computed, just read the file
    if precomputed==1
        filegist = fullfile(HOMEGIST, D(n).annotation.folder, [D(n).annotation.filename(1:end-4) '.mat']);
        if exist(filegist, 'file')
            load(filegist, 'g');
            todo = 0;
        end
    end

    % otherwise compute gist
    if todo==1
        if Nscenes>1
            disp([n Nscenes])
        end

        % load image
        try
            switch typeD
                case 1
                    img = LMimread(D, n, HOMEIMAGES);
                case 2
                    img = imread(fullfile(HOMEIMAGES, D{n}));
                case 3
                    img = D(:,:,:,n);
            end
        catch
            disp(D(n).annotation.folder)
            disp(D(n).annotation.filename)
            rethrow(lasterror)
        end

        % convert to gray scale
        img = single(mean(img,3));

        % resize and crop image to make it square
        img = imresizecrop(img, param.imageSize, 'bilinear');
        %img = imresize(img, param.imageSize, 'bilinear'); %jhhays

        % scale intensities to be in the range [0 255]
        img = img-min(img(:));
        img = 255*img/max(img(:));

        if Nscenes>1
            imshow(uint8(img))
            title(n)
        end

        % prefiltering: local contrast scaling
        output    = prefilt(img, param.fc_prefilt);

        % get gist:
        g = gistGabor(output, param);

        % save gist if a HOMEGIST file is provided
        if precomputed
            mkdir(fullfile(HOMEGIST, D(n).annotation.folder))
            save (filegist, 'g')
        end
    end

    gist(n,:) = g;
    drawnow
end

function output = prefilt(img, fc)
% ima = prefilt(img, fc);
% fc  = 4 (default)
%
% Input images are double in the range [0, 255];
% You can also input a block of images [ncols nrows 3 Nimages]
%
% For color images, normalization is done by dividing by the local
% luminance variance.

if nargin == 1
    fc = 4; % 4 cycles/image
end

w = 5;
s1 = fc/sqrt(log(2));

% Pad images to reduce boundary artifacts
img = log(img+1);
img = padarray(img, [w w], 'symmetric');
[sn, sm, c, N] = size(img);
n = max([sn sm]);
n = n + mod(n,2);
img = padarray(img, [n-sn n-sm], 'symmetric','post');

% Filter
[fx, fy] = meshgrid(-n/2:n/2-1);
gf = fftshift(exp(-(fx.^2+fy.^2)/(s1^2)));
gf = repmat(gf, [1 1 c N]);

% Whitening
output = img - real(ifft2(fft2(img).*gf));
clear img

% Local contrast normalization
localstd = repmat(sqrt(abs(ifft2(fft2(mean(output,3).^2).*gf(:,:,1,:)))), [1 1 c 1]);
output = output./(.2+localstd);

% Crop output to have same size than the input
output = output(w+1:sn-w, w+1:sm-w,:,:);

function g = gistGabor(img, param)
%
% Input:
%   img = input image (it can be a block: [nrows, ncols, c, Nimages])
%   param.w = number of windows (w*w)
%   param.G = precomputed transfer functions
%
% Output:
%   g: are the global features = [Nfeatures Nimages],
%                    Nfeatures = w*w*Nfilters*c

img = single(img);

w = param.numberBlocks;
G = param.G;
be = param.boundaryExtension;

if ndims(img)==2
    c = 1;
    N = 1;
    [nrows ncols c] = size(img);
end
if ndims(img)==3
    [nrows ncols c] = size(img);
    N = c;
end
if ndims(img)==4
    [nrows ncols c N] = size(img);
    img = reshape(img, [nrows ncols c*N]);
    N = c*N;
end

[ny nx Nfilters] = size(G);
W = w*w;
g = zeros([W*Nfilters N]);

% pad image
img = padarray(img, [be be], 'symmetric');

img = single(fft2(img));
k=0;
for n = 1:Nfilters
    ig = abs(ifft2(img.*repmat(G(:,:,n), [1 1 N])));
    ig = ig(be+1:ny-be, be+1:nx-be, :);

    v = downN(ig, w);
    g(k+1:k+W,:) = reshape(v, [W N]);
    k = k + W;
    drawnow
end

if c == 3
    % If the input was a color image, then reshape 'g' so that one column
    % is one images output:
    g = reshape(g, [size(g,1)*3 size(g,2)/3]);
end

function y=downN(x, N)
%
% averaging over non-overlapping square image blocks
%
% Input
%   x = [nrows ncols nchanels]
% Output
%   y = [N N nchanels]

nx = fix(linspace(0,size(x,1),N+1));
ny = fix(linspace(0,size(x,2),N+1));
y  = zeros(N, N, size(x,3));
for xx=1:N
  for yy=1:N
    v=mean(mean(x(nx(xx)+1:nx(xx+1), ny(yy)+1:ny(yy+1),:),1),2);
    y(xx,yy,:)=v(:);
  end
end


function [ y2, m2, d2, f2 ] = ymdf_prev_roman ( y1, m1, d1, f1 )

%*****************************************************************************80
%
%% YMDF_PREV_ROMAN returns the Roman YMDF date of the previous day.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    21 April 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer Y1, M1, D1, real F1,
%    the YMDF date.
%
%    Output, integer Y2, M2, D2, real F2,
%    yesterday's YMDF date.
%
  y2 = y1;
  m2 = m1;
  d2 = d1 - 1;
  f2 = f1;

  [ y2, m2, d2 ] = day_borrow_roman ( y2, m2, d2 );

  return
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DRAWROBOT3D(ROBOT, Q)	3D drawing with DH reference systems of the robot at the
% current joint coordinates Q. The robot parameters are stored in the
% variable ROBOT. Q = [q1 q2 ... qN] is a vector of joint values.
%
% If the robot possesses 3D graphics, the function DRAW_LINK is called to represent
% the link in 3D. Otherwise a line connecting each consecutive reference
% system is plotted.
%
% See also DENAVIT, DIRECTKINEMATIC, DRAW_LINK.
%
%   Author: Arturo Gil. Universidad Miguel Hernandez de Elche.
%   email: arturo.gil@umh.es date:   05/02/2012
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Copyright (C) 2012, by Arturo Gil Aparicio
%
% This file is part of ARTE (A Robotics Toolbox for Education).
%
% ARTE is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% ARTE 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 Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with ARTE.  If not, see <http://www.gnu.org/licenses/>.
function drawrobot3d(robot, q, noclear)
global configuration

%ligth;

%if q is not specified, then assume zero pose
if ~exist('q', 'var')
    q=zeros(1,robot.DOF)
end

h=figure(configuration.figure.robot);

%get adjusted view
[az,el] = view;

%retrieve the current zoom
zz=get(gca,'CameraViewAngle');

%delete figure
if ~exist('noclear')
    clf(h), grid on, hold on
end

xlabel('X (m)'), ylabel('Y (m)'), zlabel('Z (m)')

%apply the stored view
view(az,el);
%apply the zoom
set(gca,'CameraViewAngle', zz);

origin = [0 0 0];
destination = [];
color = ['r' 'g' 'b' 'c' 'm' 'y' 'k'];

%in case of a parallel robot, call drawrobot3d for each serial link
if isfield(robot, 'parallel')
    drawrobot3d_par(robot, q);
    return;
end

if isfield(robot, 'skip_graphics')
    return;
end

V=[];

T = robot.T0;
for i=1:robot.DOF+1

    if robot.graphical.has_graphics
        draw_link(robot, i, T);
    else
        draw_link(robot, i, T);

        destination=T(1:3,4);
        col = randperm(6);
        line([origin(1) destination(1)],[origin(2) destination(2)],[origin(3) destination(3)], 'Color',  color(col(1)) ,'LineWidth', 3 );
        origin = destination;
    end
    %draw D-H axes
    if robot.graphical.draw_axes
        draw_axes(T, sprintf('X_%d',i-1), sprintf('Y_%d',i-1), sprintf('Z_%d',i-1), robot.graphical.axes_scale);
    end
    %obtain transformation to draw the next link
    if i<= robot.DOF
       T=T*dh(robot, q, i);
    end
end
%save for later use
T06robot=T;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DRAW A TOOL AT THE ROBOT'S END
% if there is a tool attached, draw it!
% repeat the prior commands
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if isfield(robot, 'tool')

    % Activate tool. Tools must be defined in a binary way.
    % This means that they are open or closed depending
    % on an joint
    if robot.tool.tool_open
        q_tool=ones(1,robot.tool.DOF);
    else
        q_tool=zeros(1,robot.tool.DOF);
    end
    robot.tool.T0=T;
    for i=1:robot.tool.DOF+1
        if robot.tool.graphical.has_graphics
            draw_link(robot.tool, i, T);
        else
            draw_link(robot.tool, i, T);

            destination=T(1:3,4);
            col = randperm(6);
            line([origin(1) destination(1)],[origin(2) destination(2)],[origin(3) destination(3)], 'Color',  color(col(1)) ,'LineWidth', 3 );
            origin = destination;
        end
%         if robot.tool.graphical.draw_axes
%             draw_axes(T, sprintf('X_{tool%d}',i-1), sprintf('Y_{tool%d}',i-1), sprintf('Z_{tool%d}',i-1));
%         end
        %obtain transformation to draw the next link
        if (i)<= robot.tool.DOF
            T=T*dh(robot.tool, q_tool, i);
        end
    end
    %finally draw the Tool Center Point (TCP)
    if robot.tool.graphical.draw_axes
        draw_axes(T06robot*robot.tool.TCP, 'X_{toolTCP}', 'Y_{toolTCP}', 'Z_{toolTCP}');
    end

    %save for later use
    T07=T06robot*robot.tool.TCP;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  draw equipment such as tables, conveyor velts... etc
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if isfield(robot, 'equipment')
    for i=1:length(robot.equipment),
        if robot.equipment{i}.graphical.has_graphics
            draw_link(robot.equipment{i}, 1, robot.equipment{i}.T0);
        end
        if robot.equipment{i}.graphical.draw_axes
            draw_axes(robot.equipment{i}.T0, sprintf('X_{env%d}',0), sprintf('Y_{env%d}',0), sprintf('Z_{env%d}',0));
        end
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   DRAW A PIECE GRABBED
% this draws a piece grabbed at the robots end, or at the last known position
% the variable robot.tool_activated=1 asumes that the piece has been grabbed by the end tool
% robot.tool_activated==0 maintains the last known location for the pieced
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if isfield(robot, 'piece')
    for i=1:size(robot.piece,2)
        pieza_dibujar = robot.piece{i};
        if isfield(pieza_dibujar, 'graphical')
            if pieza_dibujar.graphical.has_graphics
                if isfield(robot, 'tool')
                    if robot.tool.piece_gripped==1
                        %update the last known position and orientation of the robot
                        %Trel is computed when the simulation_grip_piece function is
                        %executed. Trel is the relative position and orientation of
                        %the tool and the piece that assures that the piece is
                        %picked at a constant and visually effective orientation.
                        draw_link(pieza_dibujar, 1, T07*(robot.tool.Trel));
                    end
                else
                    draw_link(pieza_dibujar, 1, pieza_dibujar.T0);
                end
            end
        end
        if pieza_dibujar.graphical.draw_axes
            draw_axes(pieza_dibujar.T0, sprintf('X_{piece%d}',0), sprintf('Y_{piece%d}',0), sprintf('Z_{piece%d}',0));
        end
    end
end

end


function D=envidataread(datafile,info)
%ENVIDATAREAD Reads data of ENVI image file according to ENVI image file header information.
%D=ENVIDATAREAD(DATAFILE,INFO) provides image data (D).
%D will be in whatever number format (double, int, etc.) as in the ENVI
%file.

%Original version by Ian Howat, Ohio State Universtiy, ihowat@gmail.com
%Thanks to Yushin Ahn and Ray Jung
%Heavily modified by Felix Totir.

if nargin < 2
    error('You must specify the information about data (output of envihdrread)');
end

%-improved- assert(info.header_offset==0,'Non-zero header offset not supported');

%% Set binary format parameters
switch info.byte_order
    case {0}
        machine = 'ieee-le';
    case {1}
        machine = 'ieee-be';
    otherwise
        machine = 'n';
end

iscx=false; %if it is complex
switch info.data_type
    case {1}
        format = 'uint8';
    case {2}
        format= 'int16';
    case{3}
        format= 'int32';
    case {4}
        format= 'single';
    case {5}
        format= 'double';
    case {6}
        iscx=true;
        format= 'single';
    case {9}
        iscx=true;
        format= 'double';
    case {12}
        format= 'uint16';
    case {13}
        format= 'uint32';
    case {14}
        format= 'int64';
    case {15}
        format= 'uint64';
    otherwise
        error(['File type number: ',num2str(dtype),' not supported']);
end

% *** BSQ Format ***
% [Band 1]
% R1: C1, C2, C3, ...
% R2: C1, C2, C3, ...
%  ...
% RN: C1, C2, C3, ...
%
% [Band 2]
%  ...
% [Band N]

% *** BIL Format ***
% [Row 1]
% B1: C1, C2, C3, ...
% B2: C1, C2, C3, ...
%
%  ...
% [Row N]

% *** BIP Format ***
% Row 1
% C1: B1 B2 B3, ...
% C2: B1 B2 B3, ...
% ...
% Row N

n = info.lines*info.samples*info.bands;

fid=fopen(datafile,'r');
fread(fid,info.header_offset,'uint8',0,machine); %we skip the header
if ~iscx
    D = fread(fid,n,format,0,machine); %alternatively, multibandreader (matlab function) might be used
else
    D = fread(fid,2*n,format,0,machine); %alternatively, multibandreader (matlab function) might be used
    D = complex(D(1:2:end),D(2:2:end));
end
fclose(fid);

switch lower(info.interleave)
    case {'bsq'}
        D = reshape(D,[info.samples,info.lines,info.bands]);
        D = permute(D,[2,1,3]);
    case {'bil'}
        D = reshape(D,[info.samples,info.bands,info.lines]);
        D = permute(D,[3,1,2]);
    case {'bip'}
        D = reshape(D,[info.bands,info.samples,info.lines]);
        D=permute(D,[3,2,1]);
end


classdef prtClassMapLog < prtClass
 %prtClassMapLog  Maximum a Posteriori classifier with loglikelihoods
 %  returned

 %    CLASSIFIER = prtClassMap returns a Maximum a Posteriori classifier
 %
 %    CLASSIFIER = prtClassMap(PROPERTY1, VALUE1, ...) constructs a
 %    prtClassMAP object CLASSIFIER with properties as specified by
 %    PROPERTY/VALUE pairs.
 %
 %    A prtClassMap object inherits all properties from the abstract class
 %    prtClass. In addition is has the following property:
 %
 %    rvs    - A prtRv object. This property describes the random variable
 %             model used for Maximum a Posteriori classification.
 %
 %    A prtClassMap object inherits inherits the TRAIN, RUN, CROSSVALIDATE
 %    and KFOLDS methods from prtClass.
 %
 %    Example:
 %
 %    TestDataSet = prtDataGenUnimodal;       % Create some test and
 %    TrainingDataSet = prtDataGenUnimodal;   % training data
 %    classifier = prtClassMap;               % Create a classifier
 %    classifier = classifier.train(TrainingDataSet);    % Train
 %    classified = run(classifier, TestDataSet);         % Test
 %
 %    subplot(2,1,1); classifier.plot;  % Plot results
 %    subplot(2,1,2); prtScoreRoc(classified,TestDataSet);
 %    set(get(gca,'Children'), 'LineWidth',3)
 %
 %    See also prtClass, prtClassLogisticDiscriminant, prtClassBagging,
 %    prtClassMap, prtClassFld, prtClassBinaryToMaryOneVsAll, prtClassDlrt,
 %    prtClassPlsda, prtClassFld, prtClassRvm, prtClassGlrt,  prtClassSvm,
 %    prtClassTreeBaggingCap, prtClassKmsd, prtClassKnn

    properties (SetAccess=private)
        % Required by prtAction
        name = 'Maximum a Posteriori Log'   % Maximum a Posteriori
        nameAbbreviation = 'MAPLog'        % MAP
        isNativeMary = true;            % True
    end

    properties
        rvs = prtRvMvn; % Random variable object containing mean and variance
    end

    methods
        % Constructor
        function Obj = prtClassMapLog(varargin)

            Obj.classTrain = 'prtDataInterfaceCategoricalTargets';
            Obj.classRun = 'prtDataSetBase';
            Obj.classRunRetained = false;

            Obj = prtUtilAssignStringValuePairs(Obj,varargin{:});
        end
        % Set function
        function Obj = set.rvs(Obj,val)
            if ~(isa(val, 'prtRv') || isa(val, 'prtBrv'))
                error('prtClassMapLog:rvs','Rvs parameter must be of class prtRv');
            else
                Obj.rvs = val;
            end
        end
    end

    methods (Access = protected, Hidden = true)

        function Obj = trainAction(Obj,DataSet)
            % Repmat the rv objects to get one for each class
            Obj.rvs = repmat(Obj.rvs(:), (DataSet.nClasses - length(Obj.rvs)+1),1);
            Obj.rvs = Obj.rvs(1:DataSet.nClasses);

            % Get the ML estimates of the RV parameters for each class
            for iY = 1:DataSet.nClasses
                Obj.rvs(iY) = train(Obj.rvs(iY), DataSet.retainClassesByInd(iY));
            end
        end

        function DataSet = runAction(Obj,DataSet)

            logLikelihoods = zeros(DataSet.nObservations, length(Obj.rvs));
            for iY = 1:length(Obj.rvs)
                logLikelihoods(:,iY) = getObservations(run(Obj.rvs(iY), DataSet));
            end

            DataSet = prtDataSetClass(logLikelihoods);
        end

        function xOut = runActionFast(Obj,xIn,ds) %#ok<INUSD>

            xOut = zeros(size(xIn,1),length(Obj.rvs));
            for iY = 1:length(Obj.rvs)
                xOut(:,iY) = runFast(Obj.rvs(iY), xIn);
            end

            %xOut = exp(bsxfun(@minus, logLikelihoods, prtUtilSumExp(logLikelihoods.').'));
        end
    end

end


function varargout = process_simulate_ar_spectra( varargin )
% PROCESS_SIMULATE_AR_SPECTRA: Simulate source signals with an auto-regressive model
% Using this ARfit toolbox: https://climate-dynamics.org/software/#arfit
% The interactions in the AR model are defined as spectral peaks (frequency and magnitude)
% Coefficients are computed with the function DesignTwoPoles()
%
% USAGE:   OutputFiles = process_simulate_ar_spectra('Run', sProcess, sInputA)
%               signal = process_simulate_ar_spectra('Compute', b, A, C, nsamples)
%               sFiles = process_simulate_ar_spectra('Test')

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Raymundo Cassani, 2021-2022
%          Francois Tadel, 2022

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription()
    % Description the process
    sProcess.Comment     = 'Simulate AR signals';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Simulate';
    sProcess.Index       = 902;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/Connectivity2#Simulated_data';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'import'};
    sProcess.OutputTypes = {'matrix'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 0;

    % === SUBJECT NAME
    sProcess.options.subjectname.Comment = 'Subject name:';
    sProcess.options.subjectname.Type    = 'subjectname';
    sProcess.options.subjectname.Value   = 'Test';
    % === CONDITION NAME
    sProcess.options.condition.Comment = 'Condition name:';
    sProcess.options.condition.Type    = 'text';
    sProcess.options.condition.Value   = 'Simulation';
    % === NUMBER OF SAMPLES
    sProcess.options.samples.Comment = 'Number of time samples:';
    sProcess.options.samples.Type    = 'value';
    sProcess.options.samples.Value   = {12000, ' (Ntime)', 0};
    % === SAMPLING FREQUENCY
    sProcess.options.srate.Comment = 'Signal sampling frequency:';
    sProcess.options.srate.Type    = 'value';
    sProcess.options.srate.Value   = {1200, 'Hz', 2};
    % === PEAKS: FREQUENCY AND AMPLITUDE
    % === COEFFICIENT MATRIX
    sProcess.options.interactions.Comment = [
                                  'Interactions specification:
' ...
                                  'From, To  /  PeakFrequencies [Hz]  /  PeakMagnitudes [0-1]
' ...
                                  'Example:  1, 2  /  10, 20, 40  /  0.5, 0.2, 1.0
'];
    sProcess.options.interactions.Type    = 'textarea';
    sProcess.options.interactions.Value   = ['1, 1 / 10, 25 / 0.0, 1.0', 10 ...
                                             '2, 2 / 10, 25 / 1.0, 0.0', 10 ...
                                             '3, 3 / 10, 25 / 1.0, 0.2', 10 ...
                                             '1, 3 / 10, 25 / 0.0, 0.6'];
    % === DISPLAY SPECTRAL METRICS
    sProcess.options.display.Comment = {'process_simulate_ar_spectra(''DisplayMetrics'');', '
', 'View spectral metrics'};
    sProcess.options.display.Type    = 'button';
    sProcess.options.display.Value   = [];
    % === DISPLAY COEFFICIENT MATRIX
    sProcess.options.coeff.Comment = {'process_simulate_ar_spectra(''DisplayCoefficients'');', '
', 'Get coefficients matrix'};
    sProcess.options.coeff.Type    = 'button';
    sProcess.options.coeff.Value   = [];
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess)
    Comment = sProcess.Comment;
end

%% ===== GET COEFFICIENTS =====
function [A, b, C, errMsg] = GetCoefficients(sProcess)
    % Initialize returned values
    A = [];
    b = [];
    C = [];
    errMsg = '';
    % Parse interactions
    [interactions, errMsg] = process_tf_bands('ParseBands', sProcess.options.interactions.Value);
    if ~isempty(errMsg)
        return;
    end
    nInteractions = size(interactions, 1);
    ToFroms = zeros(nInteractions, 2);
    % Number of signals from interactions
    for i = 1: nInteractions
        FromTo = eval(['[' interactions{i,1} ']']);
        if length(FromTo) ~= 2
            errMsg = 'Direction must be a pair of values: From, To';
            return;
        end
        ToFroms(i, :) = fliplr(FromTo);
    end
    nSignals = max(ToFroms(:));
    % Parse definition of peaks: frequency and magnitude
    peaks = repmat(struct('freqs', [], 'mags', []), nSignals); % peaks(To, From)
    for i = 1: nInteractions
        peakFreqs = eval(['[' interactions{i,2} ']']);
        peakMags  = eval(['[' interactions{i,3} ']']);
        if length(peakFreqs) ~= length(peakMags)
            errMsg = 'Number of frequencies must match number of magnitudes';
            return;
        end
        peaks(ToFroms(i,1), ToFroms(i,2)).freqs = peakFreqs;
        peaks(ToFroms(i,1), ToFroms(i,2)).mags  = peakMags;
    end
    % Find the maximum order requested (2 * maximun_requested_peaks)
    nOrder = max(max(arrayfun(@(x) length(x.freqs),peaks))) * 2;
    % Signal sampling frequency [Hz]
    sfreq = sProcess.options.srate.Value{1};

    % Compute coefficients
    A = zeros(nSignals, nSignals, nOrder); % To, From, Order
    for iFrom = 1 : nSignals
        for iTo = 1 : nSignals
            frqs = peaks(iTo, iFrom).freqs;
            mags = peaks(iTo, iFrom).mags;
            if ~isempty(frqs)
                [~, a] = DesignTwoPoles(sfreq, frqs, mags, nOrder);
                A(iTo, iFrom, :) = -a(2:end);
            end
        end
    end
    A = reshape(A, nSignals, nSignals * nOrder);
    b = zeros(1, nSignals);
    C = eye(nSignals, nSignals);
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputA) %#ok<DEFNU>
    OutputFiles = {};
    % === GET OPTIONS ===
    % Get subject name
    SubjectName = file_standardize(sProcess.options.subjectname.Value);
    if isempty(SubjectName)
        bst_report('Error', sProcess, sInputs, 'Subject name is empty.');
        return
    end
    % Get condition name
    Condition = file_standardize(sProcess.options.condition.Value);
    % Get signal options
    nsamples = sProcess.options.samples.Value{1};
    srate   = sProcess.options.srate.Value{1};

    % Get coefficients
    [A, b, C, errMsg] = GetCoefficients(sProcess);
    if ~isempty(errMsg)
        bst_report('Error', sProcess, [], errMsg);
        return;
    end
    % Generate the signal
    try
        Data = Compute(b, A, C, nsamples);
    catch
        e = lasterr();
        bst_report('Error', sProcess, [], e);
        return;
    end

    % ===== GENERATE FILE STRUCTURE =====
    % Create empty matrix file structure
    FileMat = db_template('matrixmat');
    FileMat.Value       = Data;
    FileMat.Time        = (0:nsamples-1) ./ srate;
    FileMat.Comment     = sprintf('Simulated AR (%dx%d)', size(Data,1), nsamples);
    FileMat.Description = cell(size(Data,1),1);
    for i = 1:size(Data,1)
        FileMat.Description{i} = ['s', num2str(i)];
    end
    % Add history entry
    FileMat = bst_history('add', FileMat, 'process', 'Simulate AR signals:');
    FileMat = bst_history('add', FileMat, 'process', ['   ' strrep(sProcess.options.interactions.Value, char(10), ' ')]);

    % === OUTPUT CONDITION ===
    % Get subject
    sSubject = bst_get('Subject', SubjectName);
    % Create subject if it does not exist yet
    if isempty(sSubject)
        sSubject = db_add_subject(SubjectName);
    end
    % Default condition name
    if isempty(Condition)
        Condition = 'Simulation';
    end
    % Get condition asked by user
    [sStudy, iStudy] = bst_get('StudyWithCondition', bst_fullfile(SubjectName, Condition));
    % Condition does not exist: create it
    if isempty(sStudy)
        iStudy = db_add_condition(SubjectName, Condition, 1);
        sStudy = bst_get('Study', iStudy);
    end

    % === SAVE FILE ===
    % Output filename
    OutputFiles{1} = bst_process('GetNewFilename', bst_fileparts(sStudy.FileName), 'matrix_simar');
    % Save file
    bst_save(OutputFiles{1}, FileMat, 'v6');
    % Register in database
    db_add_data(iStudy, OutputFiles{1}, FileMat);
end

%% ===== GENERATE SIGNALS =====
% Generate synthetic AR signals
function yOut = Compute(b,A,C,samples)
    yOut = process_simulate_ar('arsim', b',A,C,samples)';
end

%% ===== DISPLAY SPECTRAL METRIC =====
function DisplayMetrics()
    % Get current process structure
    sProcess = panel_process_select('GetCurrentProcess');
    % Get options
    sfreq = sProcess.options.srate.Value{1}; % Signal sampling frequency [Hz]
    % Get coefficients
    [A,~,~,errMsg] = GetCoefficients(sProcess);
    if isempty(A)
        bst_error(['Error: Cannot compute coefficients.' 10 10 errMsg], 'Display metrics', 0);
        return;
    end
    A = reshape(A, size(A,1), size(A,1), []);
    % Display spectral metrics
    hFig = process_simulate_ar('HDisplayMetrics', A, sfreq);
end

%% ===== DISPLAY COEFFICIENTS =====
function DisplayCoefficients()
    % Get current process structure
    sProcess = panel_process_select('GetCurrentProcess');
    % Get coefficients
    [A,~,~,errMsg] = GetCoefficients(sProcess);
    if ~isempty(errMsg)
        bst_error(['Error: Cannot compute coefficients.' 10 10 errMsg], 'Display coefficients', 0);
        return;
    end

    % Titles
    strTitleA = [...
        '
Using the ARSIM function from ARFIT Toolbox (T.Schneider):
' ...
        '    X = b + A1.X1 + ... + Ap.Xp + noise

' ...
        'Coefficient matrix A =  [A1 ... Ap]
' ...
        '     - p is the order of the autorregresive model
' ...
        '     - Ai: [Nsignals_to x Nsignals_from]
'];
    strTitleB = 'Intercept b:   [1 x Nsignals]';
    strTitleC = 'Noise covariance matrix C:   [Nsignals x Nsignals]
';
    % Reshape A to [nSignals, nSignals, nOrder]
    nSignals = size(A, 1);
    A = reshape(A, nSignals, nSignals, []);
    nOrder = size(A,3);
    textA = [];
    textAtot = 'A = [';
    for iOrder = 1 : nOrder
        tmpA = A(:,:,iOrder);
        tmpTextA = evalc('disp(tmpA)');
        tmptextA = ['A', num2str(iOrder), ' = [' 10, tmpTextA(1:end-1), '];', 10];
        textA = [textA, tmptextA];
        textAtot  = [textAtot, 'A', num2str(iOrder), ' '];
    end
    textA = [textA, textAtot(1:end-1), '];'];

    % Text B
    textB = 'b = [';
    for iSignal = 1 : nSignals
        textB = [textB, '0 '];
    end
    textB = [textB(1:end-1), '];'];

    % Text C
    textC = ['C = eye(', num2str(nSignals), ', ', num2str(nSignals), ');'];

    % Using uifigure
    if exist('uitextarea', 'file')
        % Get existing specification figure
        hFig = findall(0, 'Type', 'Figure', 'Tag', 'CoeffMatrix');
        % If the figure doesn't exist yet: create it
        if isempty(hFig)
            hFig = uifigure(...
                'Name',   'AR Coefficients', ...
                'Tag',    'CoeffMatrix', ...
                'Units',  'Pixels');
        % Figure already exists: re-use it
        else
            clf(hFig);
            figure(hFig);
        end
        figpos = get(hFig, 'Position');
        wLabel = figpos(3) - 60;

        % Display A
        labelA = uilabel(hFig, ...
            'Position', [30, 260, wLabel, 220], ...
            'Text', strTitleA, ...
            'Interpreter', 'html', ...
            'FontSize', 12);
        textareaA = uitextarea(hFig, ...
            'Position',[30, 165, wLabel, 150], ...
            'Value', textA, ...
            'WordWrap', 'off');
        % Display B
        labelB = uilabel(hFig, 'Position', [30, 135, wLabel, 20], ...
                 'Text', strTitleB, ...
                 'Interpreter', 'html', 'FontSize', 12);
        textareaB = uitextarea(hFig, 'Position',[30, 105, wLabel, 30], ...
                                     'Value', textB, ...
                                     'WordWrap', 'off');
        % Display C
        labelC = uilabel(hFig, 'Position', [30, 75, wLabel, 20], ...
                 'Text', strTitleC, ...
                 'Interpreter', 'html', 'FontSize', 12);
        textareaC = uitextarea(hFig, 'Position',[30, 45, wLabel, 30], ...
                                     'Value', textC, ...
                                     'WordWrap', 'off');
    % Otherwise, just display as text
    else
        strFigure = [...
            str_striptag(strrep(strTitleA, '
', char(10))), ...
            '-------------------------------------------------' 10 ...
            textA 10, ...
            '-------------------------------------------------' 10 10, ...
            str_striptag(strrep(strTitleB, '
', char(10))), ...
            10 '-------------------------------------------------' 10 ...
            textB 10, ...
            '-------------------------------------------------' 10 10, ...
            str_striptag(strrep(strTitleC, '
', char(10))), ...
            '-------------------------------------------------' 10 ...
            textC 10 ...
            '-------------------------------------------------' 10];
        view_text(strFigure, 'AR Coefficients');
    end
end

%% ===== DESIGN TWO POLES FILTER =====
function [b, a, n_order, rads, bws, rel_gains] = DesignTwoPoles(fs, freqs, gains, n_order)
% Computes a filter with two-poles peak
% A peak is defined by its frequency and its gain
% FREQS are peak frequencies in Hz
% GAINS are relative gains in [0,1] range,
%   GAINS are relative with 1 = to gain for FS/4 peak with BW = 1Hz
%
% References
% [1] Ifeachor, EC (1993), Digital signal processing: A practical approach.
%     Chapter: Design of IIR digital filters ISBN: 020154413X
% [2] Smith, JO (2007), Introduction to digital filters: With audio applications
%     Appendix: Elementary Audio Digital Filters ISBN: 0974560715

    % Compute gain for frequency peak at Fs/4 @ BW = 1 Hz;
    bw_ref = 1;
    radius_ref = exp(-pi * bw_ref / fs);
    theta_ref  = (fs/4) / fs * 2 * pi;
    gain_ref   = 1 / ((1 - radius_ref) * sqrt(1 - (2 * radius_ref * cos(2 * theta_ref))  + (radius_ref .^ 2)));

    % Number of peaks and order of filter
    n_peaks = length(freqs);
    if isempty(n_order)
        n_order = 2 * n_peaks;
    end

    % Find radius for each peak, and compute filter
    for i_peak = 1 : n_peaks
        % Estimate radius for given peak
        peak_theta = (freqs(i_peak) / fs) * 2 * pi;
        % Range to explore
        radius_range = 0 : 0.001 : 1;
        gain_range = (1 / gain_ref) ./ ((1 - radius_range) .* sqrt(1 - (2 .* radius_range .* cos(2 * peak_theta))  + (radius_range .^ 2)));
        [~, ix] = min(abs(gain_range - gains(i_peak)));
        rads(i_peak) = radius_range(ix);
        rel_gains(i_peak) = gain_range(ix);
        bws(i_peak) = -log(rads(i_peak)) * fs / pi;
        % Two poles
        poles_peak = rads(i_peak) .* exp(1i * peak_theta);
        poles_peak = transpose([poles_peak, conj(poles_peak)]);
        % Compute one SOS
        [bs{i_peak},as{i_peak}] = zp2tf([], poles_peak, 1);
    end

    % Convolve coefficients
    a = as{1};
    b = 1;
    for i_peak = 2 : n_peaks
        a = conv(a, as{i_peak});
    end

    % Complete order if required
    if length(a) ~= n_order +1
        n_miss = n_order - (length(a) - 1);
        a = conv([1, zeros(1, n_miss)], a);
    end
end

%% ===== TEST FUNCTION =====
function sFiles = Test() %#ok<DEFNU>
    % Three-variable AR model
    sFiles = bst_process('CallProcess', 'process_simulate_ar_spectra', [], [], ...
        'subjectname', 'Test', ...
        'condition',   'Simulation', ...
        'samples',     12000, ...
        'srate',       100, ...
        'interactions' , ['1, 1 / 10, 25 / 0.0, 1.0', 10 ...
                          '2, 2 / 10, 25 / 1.0, 0.0', 10 ...
                          '3, 3 / 10, 25 / 1.0, 0.2', 10 ...
                          '1, 3 / 10, 25 / 0.0, 0.6']);
end


function [edges,idx,edgemap]=uniqedges(elem)
%
% [edges,idx,edgemap]=uniqedges(elem)
%
% return the unique edge list from a surface or tetrahedral mesh
%
% author: Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
%
% input:
%     elem: a list of elements, each row is a list of nodes for an element.
%           elem can have 2, 3 or 4 columns
%
% output:
%     edge: unique edges in the mesh, denoted by a pair of node indices
%     idx:  index of the output in the raw edge list (returned by meshedge)
%     edgemap: index of the raw edges in the output list (for triangular mesh)
%
% -- this function is part of iso2mesh toolbox (http://iso2mesh.sf.net)
%

if(size(elem)==2)
   edges=elem;
elseif(size(elem)>=3)
   edges=meshedge(elem);
else
   error('invalid input');
end

[uedges,idx,jdx]=unique(sort(edges,2),'rows');
edges=edges(idx,:);
edgemap=reshape(jdx,size(elem));


function CM = yaron_colormap(n)
  CM = hex2rgb([
  'ce517e'
  'd16486'
  'd47890'
  'd88a95'
  'dd9c9e'
  'e0ada7'
  'e5beaf'
  'ead1ba'
  'efe2c3'
  'e6e6c4'
  'cfd7bc'
  'b9ccb5'
  'a4c2af'
  '8db7a8'
  '77aca1'
  '54a49e'
  '309c96'
  '159592'
  ]);
  if nargin>=1
    CM = interp1(linspace(0,1,size(CM,1))',CM,linspace(0,1,n)','spline');
  end
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MRiLab auto generated file: DO NOT EDIT!     %
% Generated by MRiLab "DoWriteXML2m" Generator %
% MRiLab Version 1.3                           %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function dB0=Mag_GaussianHead
%====================================
p.DeltaX=0.08;
p.DeltaY=0.08;
p.DeltaZ=0.08;
p.PosX=0;
p.PosY=0;
p.PosZ=0;
p.Scale=5e-6;
dB0t=MagGaussian(p);
dB0=dB0t;
p=[];
%--------------------
end


function [tracks, revived, dropped_criteria, dropped_vicinity] = tolerance_track_lnn( eddies_path, type, time_frequency, tolerance, minimumArea ) % old args: eddies, time_frequency, tolerance
%TOLERANCE_TRACK_LNN Tracking eddies by lnn method with loss tolerance

%   ---- NOTE ----: This is not the regular version of track_lnn. This
%   version of track_lnn will interpolate fake eddies in-between two close
%   tracks. Our theory is that these nearby tracks are the same eddy, but
%   the eddy on the missing day just wasn't strong enough to be picked up
%   by the original track_lnn. We created this function to try to recover
%   some of the dropped eddies so that we could connect the broken tracks.

%   For each eddy in a timestep, all eddies in next timestep within a boundary are checked to see if there's an eddy
%   that qualifies some conditions to be stitched to current eddy. The north, south and east bounds are the gate
%   distance while the west bound is computed based on rossby-phase_speed.
% Input:
%   eddies_path: The path to the directory where eddies that were detected
%                by eddyscan were saved
%   type: Cyclone type of eddies ('anticyc' or 'cyclonic')
%   time_frequency: Number of days between timesteps. For weekly data, put
%                   7, for daily data, put 1
%   tolerance: Number of timesteps an eddy can be lost for
%   minimumArea: Minimum area of the eddies we want to include in the
%                tracking. I.E. eddy size is 4+, but we only want to
%                include eddies of size 9+ in the tracks. Set minimumArea
%                to 9
%
% Understanding the columns of the tracks
% Column 1 is the latitude of the centroid of the eddy
% Column 2 is the longitude of the centroid of the eddy
% Column 3 is the time slice (day) that the eddy is from. To use this data to
% get the actual day that the eddy is from, use this value as an index to a
% dates array, and the return value of your dates array will be the day the
% eddy is from.
% Column 4 is the index in the struct array of eddies for that particular
% day. If you get the correct eddies struct that corresponds to the date in
% column 3, the value in this column is the index in that struct where this
% actual eddy resides. It is through getting the appropriate struct and
% accessing the appropriate entry that we relate this track data to actual
% eddies in the struct.
% Column 5 is whether the eddy in the tracking data is a "fake" eddy or
% not. Normal eddies will be denoted by a '0' in the fifth column. Fake
% eddies that are generated by this tolerance_track_lnn function will have
% a '1' in the fifth column.

%% Setting constants
temp = load('rossby_daily_phase_speeds.mat');
rossby_phase_speed = temp.rossby_daily_phase_speeds;
rossby_phase_speed(:, 3) = rossby_phase_speed(:, 3) * time_frequency;

gate_distance = 150/7 * time_frequency; % 150km is default maximum travel distance for 1 week
gate_distance_in_degree = km2deg(gate_distance);

% Make lat and lon to be in [-180 180]
rossby_phase_speed(rossby_phase_speed(:, 2) > 180, 2) = rossby_phase_speed(rossby_phase_speed(:, 2) > 180, 2) - 360;
rossby_phase_speed(rossby_phase_speed(:, 2) < -180, 2) = rossby_phase_speed(rossby_phase_speed(:, 2) < -180, 2) + 360;
max_rossby_lat = max(rossby_phase_speed(:, 1));
min_rossby_lat = min(rossby_phase_speed(:, 1));

rangesearch_radius = 5; % euclidean radius in degree for rangesearch

% All next eddies with longitude in range [min_lon (min_lon + pad_range)] and [(max_lon - pad_range) max_lon] will be
% duplicated with longitude lon+360 or lon-360 to make sure Euclidean distances can still get the eddies on the other
% side of the map
pad_range = 5;
revived = 0;
dropped_criteria = 0;
dropped_vicinity = 0;

eddies_names = get_eddies_names(eddies_path, type);
eddy_length = length(eddies_names);
fake_eddy_count = ones(eddy_length, 1);
eddies = cell(eddy_length, 1);
indices = cell(eddy_length, 1);
orig_count = zeros(eddy_length, 1);
[eddies, indices, orig_count] = load_eddies_cell(eddies, indices, orig_count, eddies_names, minimumArea);

%% Begin tracking
tracks = {};

curr_track_indexes = nan(length(eddies{1}), 1); % track index of first eddies

for curr_time_step = 1:(length(eddies) - 1)
    curr_eddies_coordinates = [[eddies{curr_time_step}.Lat]' [eddies{curr_time_step}.Lon]'];
    next_eddies_coordinates = [[eddies{curr_time_step + 1}.Lat]' [eddies{curr_time_step + 1}.Lon]'];
    orig_next_eddies_coordinates_length = size(next_eddies_coordinates, 1);

    curr_real_indices = indices{curr_time_step};
    next_real_indices = indices{curr_time_step + 1};
    % eddy entries for connecting tracks.

    disp(curr_time_step)

    % The availability of next eddies, will be updated when an in-ranged eddy matches current eddy and the tracks are updated
    next_eddies_availabilities = true(size(next_eddies_coordinates, 1), 1);
    % Index of the tracks of the tracked next eddies, NaN for untracked eddies
    next_eddies_track_indexes = NaN(size(next_eddies_coordinates, 1), 1);

    % First, pad the next eddies so that euclidean distances don't mess up eddy distances at the edge of world map
    [padding_eddy_index, padding_eddies] = pad_eddies(next_eddies_coordinates, pad_range);
    padded_next_eddies = [next_eddies_coordinates; padding_eddies];

    padding_eddies_length = size(padding_eddies, 1);

    % Get in-ranged next eddies
    [next_padded_eddy_in_range_indexes, ~] = ...
        rangesearch(padded_next_eddies(:, 1:2), curr_eddies_coordinates(:, 1:2), rangesearch_radius);

    %next_padded_eddy_in_range_indexes =

    % Get distances from current eddies to in-ranged next eddies
    [dists, first_curr_eddy_index_in_distance_data] = get_eddy_distances(curr_eddies_coordinates, padded_next_eddies, ...
        next_padded_eddy_in_range_indexes);

    % Get westbound of curr eddies
    westbounds = get_west_bounds(curr_eddies_coordinates(:, 1:2), gate_distance, rossby_phase_speed, ...
        max_rossby_lat, min_rossby_lat);
    % For each current eddy, check if any in-ranged next eddy matches and update tracks
    for curr_eddy_index = 1:size(curr_eddies_coordinates, 1)
        % Index of the distance between current eddy and next eddy in the dists array
        curr_distance_index = first_curr_eddy_index_in_distance_data(curr_eddy_index);
        eddies_within_range = 0;
        matched = false;

        if curr_eddy_index <= length(curr_real_indices)
            curr_real_index = curr_real_indices(curr_eddy_index);
        else
            curr_real_index = orig_count(curr_time_step) + (curr_eddy_index - length(curr_real_indices));
        end

        for next_padded_eddy_index = next_padded_eddy_in_range_indexes{curr_eddy_index}
            % Get real next eddy index
            if next_padded_eddy_index > orig_next_eddies_coordinates_length && next_padded_eddy_index < orig_next_eddies_coordinates_length + ...
                    padding_eddies_length + 1
                % This is index of a padded eddy
                next_eddy_index = padding_eddy_index(next_padded_eddy_index - orig_next_eddies_coordinates_length);
            else
                next_eddy_index = next_padded_eddy_index;
            end

            % Check if the next eddy matches the current eddy
            if next_eddies_availabilities(next_eddy_index)
                % Only check available next eddies
                curr_eddy_lat = curr_eddies_coordinates(curr_eddy_index, 1);
                curr_eddy_lon = curr_eddies_coordinates(curr_eddy_index, 2);
                next_eddy_lat = next_eddies_coordinates(next_eddy_index, 1);
                next_eddy_lon = next_eddies_coordinates(next_eddy_index, 2);

                next_real_index = next_real_indices(next_eddy_index);

                if ~is_within_range(curr_eddy_lat, curr_eddy_lon, next_eddy_lat, next_eddy_lon, ...
                        dists(curr_distance_index), westbounds(curr_eddy_index), gate_distance, gate_distance_in_degree)
                    continue;
                else
                    eddies_within_range = eddies_within_range + 1;
                end

                if is_matched(eddies{curr_time_step}(curr_eddy_index), eddies{curr_time_step + 1}(next_eddy_index));

                    % Update tracks and get out of the loop
                    if isnan(curr_track_indexes(curr_eddy_index))
                        % new track
                        track_id = length(tracks) + 1;
                        tracks{track_id}(1, :) = ...
                            [curr_eddy_lat curr_eddy_lon curr_time_step curr_real_index];
                        tracks{track_id}(2, :) = ...
                            [next_eddy_lat next_eddy_lon (curr_time_step + 1) next_real_index];
                    else
                        % old track
                        track_id = curr_track_indexes(curr_eddy_index);
                        %check if track is flagged
                        if length(tracks{track_id}(end,:)) == 5
                            if tracks{track_id}(end,5) ~= 0
                                revived = revived + 1;
                                %need to interpolate new locations for flagged
                                %track entries
                                for i = 1:size((tracks{track_id}), 1)
                                    if tracks{track_id}(i,5) ~= 0
                                        flag = tracks{track_id}(i,5);
                                        break
                                    end
                                end

                                number_of_flags_at_end = 0;
                                for j = size(tracks{track_id},1):1
                                    if tracks{track_id}(j,5) ~= 0
                                        number_of_flags_at_end = number_of_flags_at_end + 1;
                                    else
                                        break;
                                    end
                                end

                                last_real_track_index = size(tracks{track_id},1) - number_of_flags_at_end;

                                lat_increment = (next_eddy_lat - tracks{track_id}(last_real_track_index, 1))/(number_of_flags_at_end + 1);
                                lon_increment = (next_eddy_lon - tracks{track_id}(last_real_track_index, 2))/(number_of_flags_at_end + 1);
                                %now just increment the flagged tracks' lat and lon by these amounts
                                for k = last_real_track_index + 1: size(tracks{track_id},1)
                                    tracks{track_id}(k,1) = tracks{track_id}(last_real_track_index,1) + (k - last_real_track_index)*lat_increment;
                                    tracks{track_id}(k,2) = tracks{track_id}(last_real_track_index,2) + (k - last_real_track_index)*lon_increment;
                                end
                            end
                            tracks{track_id}(end + 1, :) = ...
                                [next_eddy_lat next_eddy_lon (curr_time_step + 1) next_real_index 0 ];
                        else
                            tracks{track_id}(end + 1, :) = ...
                                [next_eddy_lat next_eddy_lon (curr_time_step + 1) next_real_index];
                        end
                    end
                    next_eddies_track_indexes(next_eddy_index) = track_id;
                    next_eddies_availabilities(next_eddy_index) = false;
                    matched = true;
                    break;
                end
                % getting here means that it had at least one eddy within
                % range but it wasn't a match for criteria

            end

            curr_distance_index = curr_distance_index + 1;

        end

        if ~matched
            if ~isnan(curr_track_indexes(curr_eddy_index))
                % getting here means there were no matches
                if ~eddies_within_range
                    dropped_vicinity = dropped_vicinity + 1;
                else
                    %there was at least one within range but none were matches
                    dropped_criteria = dropped_criteria + 1;
                end
            end

            if tolerance > 0
                %if a track already exists, add flag for potential
                %missing track.
                %This section checks if it has the max tolerated
                %number of flagged tracks already
                if ~isnan(curr_track_indexes(curr_eddy_index))
                    track_id = curr_track_indexes(curr_eddy_index);

                    if length(tracks{track_id}(end,:)) == 5  % protect against out of bounds indexing (too large)
                        if size(tracks{track_id}, 1) > tolerance %protect against negative indexing
                            if tracks{track_id}(end - tolerance + 1, 5) ~= 0
                                %remove flagged tracks

                                tracks{track_id} = tracks{track_id}(1: (end - tolerance),:);
                                continue;
                            end
                        end
                    else
                        %track isn't flagged at all, so add 5th column in order to add flags
                        tracks{track_id}(:,5) = 0;
                    end
                    %won't get here if it got trimmed due to the
                    %continue statement above
                    %now make entry to next coordinates and eddies arrays so we can
                    %search from this fake eddy in next timestep
                    lat_increment = tracks{track_id}(end, 1) - tracks{track_id}(end - 1, 1);
                    lon_increment = tracks{track_id}(end, 2) - tracks{track_id}(end - 1, 2);
                    next_eddies_coordinates(end+1,:) = [curr_eddies_coordinates(curr_eddy_index, 1) + lat_increment ...
                        curr_eddies_coordinates(curr_eddy_index, 2) + lon_increment];
                    %now add the stats of the current eddy to the
                    %end of the next time steps eddies, but alter
                    %the coordinates
                    eddies{curr_time_step + 1}(end+1) = eddies{curr_time_step}(curr_eddy_index);
                    eddies{curr_time_step + 1}(end).Lat = curr_eddies_coordinates(curr_eddy_index,1) + lat_increment;%next_eddies_coordinates(end,1);
                    eddies{curr_time_step + 1}(end).Lon = curr_eddies_coordinates(curr_eddy_index,2) + lon_increment;%next_eddies_coordinates(end,2);
                    %make a flagged track entry with the above interpolated stats
                    if ~eddies_within_range
                        flag = -1; %dropped for vicinity
                    else
                        flag = 1; % dropped for criteria
                    end
                    next_size = orig_count(curr_time_step + 1) + fake_eddy_count(curr_time_step + 1);
                    fake_eddy_count(curr_time_step + 1) = fake_eddy_count(curr_time_step + 1) + 1;
                    tracks{track_id}(end + 1, :) = ...
                        [next_eddies_coordinates(end, 1) next_eddies_coordinates(end, 2)...
                        (curr_time_step + 1) next_size flag];
                    next_eddies_track_indexes(length(eddies{curr_time_step + 1})) = track_id;
                    next_eddies_availabilities(size(next_eddies_coordinates), 1) = false;
                end
            end

        end
    end
    curr_track_indexes = next_eddies_track_indexes;
    eddies{curr_time_step} = [];
    [eddies, indices, orig_count] = load_eddies_cell(eddies, indices, orig_count, eddies_names, minimumArea);
end

for i = 1:length(tracks)
    track = tracks{i};
    [~,b] = size(track);
    if b < 5
        track(:, 5) = 0;
        tracks{i} = track;
    end
end

end

function within_range = is_within_range(curr_lat, curr_lon, next_lat, next_lon, km_distance, westbound, ...
    gate_distance, gate_distance_in_degree)
% An approximate way of checking if the eddy in next timestep is within edddy in current timestep's range,
% following the description in D.B. Chelton et al. / Progress in Oceanography 91 (2011) page 208
% The north, east and south bounds are defined by the gate distance while the westbound is precomputed by rossby
% phase speed.
% Input:
%   curr_lat: latitude of the eddy in current timestep
%   curr_lon: longitude of the eddy in current timestep
%   next_lat: latitude of the eddy in next timestep
%   next_lon: longitude of the eddy in next timestep
%   km_distance: distance in km between the eddies in current and next timestep
%   gate_distance: default value of how far an eddy can travel in one timestep in km
%   gate_distance: default value of how far an eddy can travel in one timestep in degree

% Check north and south bound
if next_lat > curr_lat + gate_distance_in_degree || next_lat < curr_lat - gate_distance_in_degree
    within_range = false; % Avoid using km2deg or deg2km because
else
    % Check east and west bound
    if abs(next_lon - curr_lon) > 180
        if next_lon > curr_lon
            curr_lon = curr_lon + 360;
        else
            next_lon = next_lon + 360;
        end
    end
    if next_lon >= curr_lon
        is_on_east_side = true;
    else
        is_on_east_side = false;
    end

    if is_on_east_side
        within_range = km_distance <= gate_distance;
    else
        within_range = km_distance <= westbound;
    end

end

end

function westbounds = get_west_bounds(eddy_coordinates, gate_distance, phase_speed_data, max_rossby_lat, min_rossby_lat)
% Getting westbound of eddies by eddy coordinates and rossby phasespeed data
% West bound of an eddy is defined as the maximum value between 1.75 * rossby_phase_speed and default gate_distance
%   eddy_coordinates: coordinates of eddies in format [lat lon]
%   gate_distance: the default maximum distance an eddy can travel in a single timestep
%   phase_speed_data: rossby phase speed data in format [lat lon speed]
%   max_rossby_lat: to make sure eddies with latitude out of range get the default gate distance
%   min_rossby_lat: to make sure eddies with latitude out of range get the default gate distance

curr_eddy_lats = eddy_coordinates(:, 1);
curr_eddy_lons = eddy_coordinates(:, 2);
% Convert eddy longtidue format to -180 to 180 to make sure knn search work
curr_eddy_lons(curr_eddy_lons > 180) = curr_eddy_lons(curr_eddy_lons > 180) - 360;
curr_eddy_lons(curr_eddy_lons < -180) = curr_eddy_lons(curr_eddy_lons < -180) + 360;

nearest_ind = knnsearch(phase_speed_data(:, 1:2), [curr_eddy_lats curr_eddy_lons]);
westbounds = 1.75 * phase_speed_data(nearest_ind, 3);
westbounds(westbounds < gate_distance) = gate_distance;
% Make sure out of range eddies get the default value
westbounds(curr_eddy_lats > max_rossby_lat | curr_eddy_lats < min_rossby_lat) = gate_distance;

end

function [dists, first_curr_eddy_index_in_distance_data] = get_eddy_distances(curr_eddies, next_eddies, ...
    next_eddies_indexes)
% Get distances from current eddies to next eddies with indexes in the cell array next_eddies_indexes. This function
% computes the distance in one distance and deg2km to increase the tracking function performance
% Also return an array of indexes of the first appearance of a curr eddy in the distance array
% Input:
%   curr_eddies: eddies in current timestep with format [lat lon ...]
%   next_eddies: eddies in next timestep with format [lat lon ...]
%   next_eddies_indexes: a cell array of indexes of next eddies that will be computed the distances to current eddies

next_eddy_indexes_lengths = cellfun('length', next_eddies_indexes);
vectorized_next_eddy_indexes = [next_eddies_indexes{:}]';
vectorized_curr_eddy_indexes = zeros(sum(next_eddy_indexes_lengths), 1);
curr_index = 1;
for i = 1:length(next_eddy_indexes_lengths)
    vectorized_curr_eddy_indexes(curr_index:(curr_index + next_eddy_indexes_lengths(i) - 1)) = i;
    curr_index = curr_index + next_eddy_indexes_lengths(i);
end

dists = deg2km(distance(curr_eddies(vectorized_curr_eddy_indexes, 1:2), ...
    next_eddies(vectorized_next_eddy_indexes, 1:2)));

first_curr_eddy_index_in_distance_data = ones(length(next_eddy_indexes_lengths), 1);
for i = 2:length(next_eddy_indexes_lengths)
    first_curr_eddy_index_in_distance_data(i) = first_curr_eddy_index_in_distance_data(i - 1) + next_eddy_indexes_lengths(i - 1);
end

end

function [padding_eddy_index, padding_eddies] = pad_eddies(eddies, pad_range)
% Duplicate eddies in range [min_lon (min_lon + pad_range)] and [(max_lon - pad_range) max_lon] to make sure knnsearch
% work correctly to get in-range eddies in next timestep
%   eddies: eddies in format [lat lon ...]
%   pad_range: how far we want to pad (in degree)

min_lon = min(eddies(:, 2));
max_lon = max(eddies(:, 2));
padding_eddy_index = find( (eddies(:, 2) >= min_lon & eddies(:, 2) <= (min_lon + pad_range)) ...
    | (eddies(:, 2) >= (max_lon - pad_range) & eddies(:, 2) <= max_lon ) );

padding_eddies = eddies(padding_eddy_index, :);
padding_eddies(padding_eddies(:, 2) <= (min_lon + pad_range), 2) = ...
    padding_eddies(padding_eddies(:, 2) <= (min_lon + pad_range), 2) + 360;
padding_eddies(padding_eddies(:, 2) >= (max_lon - pad_range), 2) = ...
    padding_eddies(padding_eddies(:, 2) >= (max_lon - pad_range), 2) - 360;

end

function matched = is_matched(curr_eddy, next_eddy)
% An eddy is consider matched with current eddy if the amplitude and surface area is in range [0.25 2.75] *
% curr_eddy_amp/surface_area
%   curr_eddy: an eddy in current timestep in format [lat lon amp surface_area]
%   next_eddy: an eddy in next timestep in format [lat lon amp surface_area]

curr_amplitude = curr_eddy.Amplitude;
next_amplitude = next_eddy.Amplitude;
curr_surface_area = curr_eddy.SurfaceArea;
next_surface_area = next_eddy.SurfaceArea;
%if isempty(curr_amplitude) | isempty(next_amplitude) | isempty
%end
matched_amp = curr_amplitude > .25* next_amplitude && curr_amplitude < 2.75 * next_amplitude;
matched_area = curr_surface_area > .25 * next_surface_area && curr_surface_area < 2.75 * next_surface_area;

% matched = curr_amplitude > 0.25 * next_amplitude && curr_amplitude < 2.75 * next_amplitude ...
%     && curr_surface_area > 0.25 * next_surface_area && curr_surface_area < 2.75 * next_surface_area;
matched = matched_amp && matched_area;
end

function [eddies_names] = get_eddies_names(path, type)
% path is the path to the eddies directory
% type is anticyclonic or cyclonic
if ~strcmp(path(end), '/')
    path = strcat(path, '/');
end
files = dir(path);
x = 0;
for i = 1:length(files)
    if ~isempty(strfind(files(i).name, [type, '_']))
        x = x + 1;
    end
end
eddies_names = cell(x, 1);
x = 1;
for i = 1:length(files)
    if files(i).isdir && ~isequal(files(i).name, '.') && ~isequal(files(i).name, '..')
        rec_names = get_eddies_names([path, files(i).name, '/'], type);
        for j = 1:length(rec_names)
            eddies_names{x} = rec_names{j};
            x = x + 1;
        end
        continue;
    end
    file = files(i).name;
    [~, name, ext] = fileparts([path, file]);
    if ~isempty(strfind(name, [type, '_'])) && strcmp(ext, '.mat')
        eddies_names{x} = [path, file];
        x = x + 1;
    end
end
end

function [modified_eddies_cell, modified_indices_cell, modified_orig_count_array] = load_eddies_cell(eddies_cell, real_indices_cell, orig_count_array, eddies_names, minimumArea)
count_to_load = 10;
names_length = length(eddies_names);
x = 0;
pos_1 = 1;
for i = 1:length(eddies_cell)
    if ~isempty(eddies_cell{i})
        if x == 0
            pos_1 = i;
        end
        x = x + 1;
    end
end
iterations = count_to_load - x;
first_empty_pos = pos_1 + x;
last_empty_pos = first_empty_pos + iterations - 1;
if last_empty_pos > names_length
    last_empty_pos = names_length;
end
for i = first_empty_pos:last_empty_pos
    eddy_name = eddies_names{i};
    vars = load(eddy_name);
    names = fieldnames(vars);
    eddies = vars.(names{1});
    [f_eddies, real_indices, orig_count] = filter_eddies(eddies, minimumArea);
    eddies_cell{i} = f_eddies;
    real_indices_cell{i} = real_indices;
    orig_count_array(i) = orig_count;
end
modified_eddies_cell = eddies_cell;
modified_indices_cell = real_indices_cell;
modified_orig_count_array = orig_count_array;
end

function [filtered_eddies, real_indices, original_eddy_count] = filter_eddies(eddies, minimumArea)
stats = [eddies.Stats];
areas = [stats.Area];
x = 1;
original_eddy_count = length(eddies);
for i = 1:length(eddies)
    if areas(i) >= minimumArea
        filtered_eddies(x) = eddies(i);
        real_indices(x) = i;
        x = x + 1;
    end
end
end