% BOOTSTAT - accumulate surrogate data to assess significance by permutation of some
%              measure of two input variables.
%
%              If 'distfit','on', fits the psd with a 4th-order polynomial using the
%              data kurtosis, as in Ramberg, J.S., Tadikamalla, P.R., Dudewicz E.J.,
%              Mykkytka, E.F. "A probability distribution and its uses in fitting data."
%              Technometrics, 21:201-214, 1979.
% Usage:
%            >> [rsignif,rboot] = bootstat( { arg1 arg2 ...}, formula, varargin ...);
% Inputs:
%    arg1    - [array] 1-D, 2-D or 3-D array of values
%    arg2    - [array] 1-D, 2-D or 3-D array of values
%    formula - [string] formula to compute the given measure. Takes arguments
%                   'arg1', 'arg2' as inputs and 'res' (result, by default) as output.
%                   For data arrays of more than 1 dimension, the formula must be iterative
%                   so that shuffling can occur at each step while scanning the last
%                   array dimension.  Examples:
%                   'res = arg1 - arg2'              % difference of two 1-D data arrays
%                   'res = mean( arg1 .* arg2)'      % mean projection of two 1-D data arrays
%                   'res = res + arg1 .* conj(arg2)' % iterative, for use with 2|3-D arrays
% Optional inputs:
%   'boottype '   - ['rand'|'shuffle']
%                   'rand'  = do not shuffle data. Only flip polarity randomly (for real
%                             number) or phase (for complex numbers).
%                   'shuffle' = shuffle values of first argument (see two options below).
%                   Default.
%   'shuffledim'  - [integer] indices of dimensions to shuffle. For instance, [1 2] will
%                   shuffle the first two dimensions. Default is to shuffle along
%                   dimension 2.
%   'shufflemode' - ['swap'|'regular'] shuffle mode. Either swap dimensions (for instance
%                   swap rows then columns if dimension [1 2] are selected) or shuffle
%                   in each dimension independently (slower). If only one dimension is
%                   selected for shuffling, this option does not change the result.
%   'randmode'    - ['opposite'|'inverse'] randomize sign (or phase for complex number,
%                   or randomly set half the value to reference.
%   'alpha'       - [real] significance level (between 0 and 1) {default 0.05}.
%   'naccu'       - [integer] number of exemplars to accumulate {default 200}.
%   'bootside'    - ['both'|'upper'] side of the surrogate distribution to
%                   consider for significance. This parameter affects the size
%                   of the last dimension of the accumulation array ('accres')
%                   (size is 2 for 'both' and 1 for 'upper') {default: 'both'}.
%   'basevect'    - [integer vector] time vector indices for baseline in second dimension.
%                   {default: all time points}.
%   'rboot'       - accumulation array (from a previous call). Allows faster
%                   computation of the 'rsignif' output {default: none}.
%   'formulaout'  - [string] name of the computed variable {default: 'res'}.
%   'dimaccu'     - [integer] use dimension in result to accumulate data.
%                   For instance if the result array is size [60x50] and this value is 2,
%                   the function will consider than 50 times 60 value have been accumulated.
%
% Fitting distribution:
%   'distfit'     - ['on'|'off'] fit distribution with known function to compute more accurate
%                   limits or exact p-value (see 'vals' option). The MATLAB statistical toolbox
%                   is required. This option is currently implemented only for 1-D data.
%   'vals'        - [float array] significance values. 'alpha' is ignored and
%                   rsignif returns the p-values. Requires 'distfit' (see above).
%                   This option currently implemented only for 1-D data.
%   'correctp'    - [phat pci zerofreq] parameters for correcting for a biased probability
%                   distribution (requires 'distfit' above). See help of CORRECTFIT.
% Outputs:
%    rsignif      - significance arrays. 2 values (low high) for each point (use
%                   'alpha' to change these limits).
%    rboot        - accumulated surrogate data values.
%
% Authors: Arnaud Delorme, Bhaktivedcanta Institute, Mumbai, India, Nov 2004
%
% See also: TIMEF

% NOTE: There is an undocumented parameter, 'savecoher', [0|1]
% HELP TEXT REMOVED:  (Ex: Using option 'both', coherence during baseline would be
%                      ignored since times are shuffled during each accumulation.

% Copyright (C) 9/2002  Arnaud Delorme & Scott Makeig, SCCN/INC/UCSD
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

% *************************************
% To fit the psd with as 4th order polynomial using the distribution kurtosis,
% Reference: Ramberg, J.S., Tadikamalla, P.R., Dudewicz E.J., Mykkytka, E.F.
% "A probability distribution and its uses in fitting data."
% Technimetrics, 1979, 21: 201-214.
% *************************************

function [accarrayout, Rbootout, Rbootout2] = bootstat(oriargs, formula, varargin)
%	nb_points, timesout, naccu, baselength, baseboot, boottype, alpha, rboot);

if nargin < 2
	help bootstat;
	return;
end

if ~ischar(formula)
	error('The second argument must be a string formula');
end

g = finputcheck(varargin, ...
                { 'dims'          'integer'  []                       []; ...
                  'naccu'         'integer'  [0 10000]                200; ...
                  'bootside'      'string'   { 'both','upper' }       'both'; ...
                  'basevect'      'integer'  []                       []; ...
				  'boottype'      'string'  { 'rand','shuffle' }      'shuffle'; ...
				  'shufflemode'   'string'  { 'swap','regular' }      'swap'; ...
				  'randmode'      'string'  { 'opposite','inverse' }  'opposite'; ...
				  'shuffledim'    'integer'  [0 Inf]                  []; ...
				  'label'         'string'   []                       formula; ...
				  'alpha'         'real'     [0 1]                    0.05; ...
				  'vals'          'real'     []                       []; ...
				  'distfit'       'string'   {'on','off' }            'off'; ...
				  'dimaccu'       'integer'  [1 Inf]                  []; ...
				  'correctp'      'real'     []                       []; ...
				  'rboot'         'real'     []                       NaN	});
if ischar(g)
	error(g);
end
if isempty(g.shuffledim) && strcmpi(g.boottype, 'rand')
    g.shuffledim = [];
elseif isempty(g.shuffledim)
    g.shuffledim = 2;
end;
unitname = '';
if 2/g.alpha > g.naccu
    if strcmpi(g.distfit, 'off') || ~((size(oriarg1,1) == 1 || size(oriarg1,2) == 1) && size(oriarg1,3) == 1)
        g.naccu = 2/g.alpha;
        fprintf('Adjusting naccu to compute alpha value');
    end
end
if isempty(g.rboot)
	g.rboot = NaN;
end

% function for bootstrap computation
% ----------------------------------
if ~iscell(oriargs) || length(oriargs) == 1,
    oriarg1 = oriargs;
    oriarg2 = [];
else
    oriarg1 = oriargs{1};
    oriarg2 = oriargs{2};
end
[nb_points times trials] = size(oriarg1);
if times == 1, disp('Warning 1 value only for shuffling dimension'); end

% only consider baseline
% ----------------------
if ~isempty(g.basevect)
    fprintf('\nPermutation statistics baseline length is %d (out of %d) points\n', length(g.basevect), times);
    arg1 = oriarg1(:,g.basevect,:);
    if ~isempty(oriarg2)
        arg2 = oriarg2(:,g.basevect,:);
    end
else
    arg1 = oriarg1;
    arg2 = oriarg2;
end

% formula for accumulation array
% ------------------------------
% if g.dimaccu is not empty, accumulate over that dimension
% of the resulting array to speed up computation
formula = [ 'res=' formula ];
g.formulapost = [ 'if index == 1, ' ...
                  '   if ~isempty(g.dimaccu), ' ...
                  '      Rbootout= zeros([ ceil(g.naccu/size(res,g.dimaccu)) size( res ) ]);' ...
                  '   else,' ...
                  '      Rbootout= zeros([ g.naccu size( res ) ]);' ...
                  '   end;' ...
                  'end,' ...
                  'Rbootout(count,:,:,:) = res;' ...
                  'count = count+1;' ...
                  'if ~isempty(g.dimaccu), ' ...
                  '   index = index + size(res,g.dimaccu);' ...
                  '   fprintf(''%d '', index-1);' ...
                  'else ' ...
                  '   index=index+1;' ...
                  '   if rem(index,10)  == 0, fprintf(''%d '', index); end;' ...
                  '   if rem(index,100) == 0, fprintf(''\n''); end;' ...
                  'end;' ];

% **************************
% case 1: precomputed values
% **************************
if ~isnan(g.rboot)
    Rbootout = g.rboot;
% ***********************************
% case 2: randomize polarity or phase
% ***********************************
elseif strcmpi(g.boottype, 'rand') && strcmpi(g.randmode, 'inverse')
    fprintf('Bootstat function: randomize inverse values\n');
    fprintf('Processing permutation statistics for %s (naccu=%d):', g.label, g.naccu);

    % compute random array
    % --------------------
    multarray = ones(size(arg1));
    totlen    = prod(size(arg1));
    if isreal(arg1),
        multarray(1:round(totlen/2)) = 0;
    end
    for shuff = 1:ndims(multarray)
         multarray = supershuffle(multarray,shuff); % initial shuffling
    end
    if isempty(g.shuffledim), g.shuffledim = 1:ndims(multarray); end
    invarg1 = 1./arg1;

    % accumulate
    % ----------
    index = 1;
    count = 1;
    while index <= g.naccu
        for shuff = g.shuffledim
            multarray = supershuffle(multarray,shuff);
        end
        tmpinds = find(reshape(multarray, 1, prod(size(multarray))));
        arg1 = oriarg1;
        arg1(tmpinds) = invarg1(tmpinds);
        eval([ formula ';' ]);
        eval( g.formulapost ); % also contains index = index+1
    end
elseif strcmpi(g.boottype, 'rand') % opposite
    fprintf('Bootstat function: randomize polarity or phase\n');
    fprintf('Processing permutation statistics for %s (naccu=%d):', g.label, g.naccu);

    % compute random array
    % --------------------
    multarray = ones(size(arg1));
    totlen    = prod(size(arg1));
    if isreal(arg1),
        multarray(1:round(totlen/2)) = -1;
    else
        tmparray            = exp(j*linspace(0,2*pi,totlen+1));
        multarray(1:totlen) = tmparray(1:end-1);
    end
    for shuff = 1:ndims(multarray)
         multarray = supershuffle(multarray,shuff); % initial shuffling
    end
    if isempty(g.shuffledim), g.shuffledim = 1:ndims(multarray); end

    % accumulate
    % ----------
    index = 1;
    count = 1;
    while index <= g.naccu
        for shuff = g.shuffledim
            multarray = supershuffle(multarray,shuff);
        end
        arg1 = arg1.*multarray;
        eval([ formula ';' ]);
        eval( g.formulapost ); % also contains index = index+1
    end
% ********************************************
% case 3: shuffle vector of only one dimension
% ********************************************
elseif length(g.shuffledim) == 1
    fprintf('Bootstat function: shuffling along dimension %d only\n', g.shuffledim);
    fprintf('Processing permutation statistics for %s (naccu=%d):', g.label, g.naccu);

    index = 1;
    count = 1;
    while index <= g.naccu
        arg1 = shuffleonedim(arg1,g.shuffledim);
        eval([ formula ';' ]);
        eval( g.formulapost );
    end
% ***********************************************
% case 5: shuffle vector along several dimensions
% ***********************************************
else
    if strcmpi(g.shufflemode, 'swap') % swap mode
        fprintf('Bootstat function: shuffling along dimension %s (swap mode)\n', int2str(g.shuffledim));
        fprintf('Processing permutation statistics for %s (naccu=%d):', g.label, g.naccu);
        index = 1;
        count = 1;
        while index <= g.naccu
            for shuff = g.shuffledim
                arg1 = supershuffle(arg1,shuff);
            end
            eval([ formula ';' ]);
            eval( g.formulapost );
        end
    else  % regular shuffling
        fprintf('Bootstat function: shuffling along dimension %s (regular mode)\n', int2str(g.shuffledim));
        fprintf('Processing permutation statistics for %s (naccu=%d):', g.label, g.naccu);
        index = 1;
        count = 1;
        while index <= g.naccu
            for shuff = g.shuffledim
                arg1 = shuffleonedim(arg1,shuff);
            end
            eval([ formula ';' ]);
            eval( g.formulapost );
        end
    end
end
Rbootout(count:end,:,:,:) = [];

% **********************
% assessing significance
% **********************

% get accumulation array
% ----------------------
accarray = Rbootout;
if ~isreal(accarray)
    accarray = sqrt(accarray .* conj(accarray)); % faster than abs()
end
% reshape the output if necessary
% -------------------------------
if ~isempty(g.dimaccu)
    if g.dimaccu+1 == 3
        accarray = permute( accarray, [1 3 2]);
    end
    accarray = reshape( accarray, size(accarray,1)*size(accarray,2), size(accarray,3) );
end
if size(accarray,1) == 1, accarray = accarray'; end; % first dim contains g.naccu

% ******************************************************
% compute thresholds on array not fitting a distribution
% ******************************************************
if strcmpi(g.distfit, 'off')

    % compute bootstrap significance level
    % ------------------------------------
    accarray  = sort(accarray,1); % always sort on naccu
    Rbootout2 = accarray;
    i         = round(size(accarray,1)*g.alpha);
    accarray1 = squeeze(mean(accarray(size(accarray,1)-i+1:end,:,:),1));
    accarray2 = squeeze(mean(accarray(1:i                     ,:,:),1));
    if abs(accarray(1,1,1) - accarray(end,1,1)) < abs(accarray(1,1,1))*1e-15
        accarray1(:) = NaN;
        accarray2(:) = NaN;
    end

else
    % *******************
    % fit to distribution
    % *******************
    sizerboot   = size (accarray);
    accarray1   = zeros(sizerboot(2:end));
    accarray2   = zeros(sizerboot(2:end));

    if ~isempty(g.vals{index})
        if ~all(size(g.vals{index}) == sizerboot(2:end) )
            error('For fitting, vals must have the same dimension as the output array (try transposing)');
        end
    end

    % fitting with Ramberg-Schmeiser distribution
    % -------------------------------------------
    if ~isempty(g.vals{index}) % compute significance for value
        for index1 = 1:size(accarrayout,1)
            for index2 = 1:size(accarrayout,2)
                accarray1(index1,index2) = 1 - rsfit(squeeze(accarray(:,index1,index2)), g.vals{index}(index1, index2));
                if length(g.correctp) == 2
                    accarray1(index1,index2) = correctfit(accarray1, 'gamparams', [g.correctp 0]); % no correction for p=0
                else
                    accarray1(index1,index2) = correctfit(accarray1, 'gamparams', g.correctp);
                end
            end
        end
    else % compute value for significance
        for index1 = 1:size(accarrayout,1)
            for index2 = 1:size(accarrayout,2)
                [p c l chi2] = rsfit(Rbootout(:),0);
                pval = g.alpha;   accarray1(index1,index2) = l(1) + (pval.^l(3) - (1-pval).^l(4))/l(2);
                pval = 1-g.alpha; accarray2(index1,index2) = l(1) + (pval.^l(3) - (1-pval).^l(4))/l(2);
            end
        end
    end

    % plot results
    % -------------------------------------
    % figure;
    % hist(abs(Rbootout)); tmpax = axis;
    % hold on;
    % valcomp = linspace(min(abs(Rbootout(:))), max(abs(Rbootout(:))), 100);
    % normy = normpdf(valcomp, mu, sigma);
    % plot(valcomp, normy/max(normy)*tmpax(4), 'r');
    % return;
end

% set output array: backward compatible
% -------------------------------------
if strcmpi(g.bootside, 'upper'); % only upper significance
    accarrayout = accarray1;
else
    if size(accarray1,1) ~= 1 && size(accarray1,2) ~= 1
        accarrayout        = accarray2;
        accarrayout(:,:,2) = accarray1;
    else
        accarrayout = [ accarray2(:) accarray1(:) ];
    end
end
accarrayout = squeeze(accarrayout);
if size(accarrayout,1) == 1 && size(accarrayout,3) == 1, accarrayout = accarrayout'; end

% better but not backward compatible
% ----------------------------------
% accarrayout = { accarray1 accarray2 };

return;

    % fitting with normal distribution (deprecated)
    % --------------------------------
    [mu sigma] = normfit(abs(Rbootout(:)));
    accarrayout = 1 - normcdf(g.vals, mu, sigma); % cumulative density distribution
                                        % formula of normal distribution
                                        % y = 1/sqrt(2) * exp( -(x-mu).^2/(sigma*sigma*2) ) / (sqrt(pi)*sigma);
% % Gamma and Beta fits:
% elseif strcmpi(g.distfit, 'gamma')
%  [phatgam pcigam] = gamfit(abs(Rbootout(:)));
%  gamy = gampdf(valcomp, phatgam(1), pcigam(2))
%  p = 1 - gamcdf(g.vals, phatgam(1), pcigam(2)); % cumulative density distribution
% elseif strcmpi(g.distfit, 'beta')
%  [phatbeta pcibeta] = betafit(abs(Rbootout(:)));
%  betay = betapdf(valcomp, phatbeta(1), pcibeta(1));
%  p = 1 - betacdf(g.vals, phatbeta(1), pcibeta(1)); % cumulative density distribution
% end

    if strcmpi(g.distfit, 'off')
        tmpsort = sort(Rbootout);
        i = round(g.alpha*g.naccu);
        sigval = [mean(tmpsort(1:i)) mean(tmpsort(g.naccu-i+1:g.naccu))];
        if strcmpi(g.bootside, 'upper'), sigval = sigval(2); end
        accarrayout = sigval;
    end

% this shuffling preserve the number of -1 and 1
% for columns and rows (assuming matrix size is multiple of 2
% -----------------------------------------------------------
function array = supershuffle(array, dim)
    if size(array, 1) == 1 || size(array,2) == 1
        array = shuffle(array);
        return;
    end
    if size(array, dim) == 1, return; end

    if dim == 1
        indrows = shuffle(1:size(array,1));
        for index = 1:2:length(indrows)-rem(length(indrows),2) % shuffle rows
            tmparray                    = array(indrows(index),:,:);
            array(indrows(index),:,:)   = array(indrows(index+1),:,:);
            array(indrows(index+1),:,:) = tmparray;
        end
    elseif dim == 2
        indcols = shuffle(1:size(array,2));
        for index = 1:2:length(indcols)-rem(length(indcols),2) % shuffle columns
            tmparray                    = array(:,indcols(index),:);
            array(:,indcols(index),:)   = array(:,indcols(index+1),:);
            array(:,indcols(index+1),:) = tmparray;
        end
    else
        ind3d = shuffle(1:size(array,3));
        for index = 1:2:length(ind3d)-rem(length(ind3d),2) % shuffle columns
            tmparray                  = array(:,:,ind3d(index));
            array(:,:,ind3d(index))   = array(:,:,ind3d(index+1));
            array(:,:,ind3d(index+1)) = tmparray;
        end
    end

% shuffle one dimension, one row/columns at a time
% -----------------------------------------------
function array = shuffleonedim(array, dim)
    if size(array, 1) == 1 || size(array,2) == 1
        array = shuffle(array, dim);
    else
        if dim == 1
            for index1 = 1:size(array,3)
                for index2 = 1:size(array,2)
                    array(:,index2,index1) = shuffle(array(:,index2,index1));
                end
            end
        elseif dim == 2
            for index1 = 1:size(array,3)
                for index2 = 1:size(array,1)
                    array(index2,:,index1) = shuffle(array(index2,:,index1));
                end
            end
        else
            for index1 = 1:size(array,1)
                for index2 = 1:size(array,2)
                    array(index1,index2,:) = shuffle(array(index1,index2,:));
                end
            end
        end
    end


function x = invpowspec(y, sr, wintime, steptime, excit)
%x = invpowspec(y, sr, wintime, steptime, excit)
%
% Attempt to go back from specgram-like powerspec to audio waveform
% by scaling specgram of white noise
%
% default values:
% sr = 8000Hz
% wintime = 25ms (200 samps)
% steptime = 10ms (80 samps)
% which means use 256 point fft
% hamming window
%
% excit is input excitation; white noise is used if not specified

% for sr = 8000
%NFFT = 256;
%NOVERLAP = 120;
%SAMPRATE = 8000;
%WINDOW = hamming(200);

[nrow, ncol] = size(y);

if nargin < 2
  sr = 8000;
end
if nargin < 3
  wintime = 0.025;
end
if nargin < 4
  steptime = 0.010;
end
if nargin < 5
  r = [];
else
  r = excit;
end

winpts = round(wintime*sr);
steppts = round(steptime*sr);

NFFT = 2^(ceil(log(winpts)/log(2)));

if NFFT ~= 2*(nrow-1)
  disp('Inferred FFT size doesn''t match specgram');
end

NOVERLAP = winpts - steppts;
SAMPRATE = sr;

% Values coming out of rasta treat samples as integers,
% not range -1..1, hence scale up here to match (approx)
%y = abs(specgram(x*32768,NFFT,SAMPRATE,WINDOW,NOVERLAP)).^2;

xlen = winpts + steppts*(ncol - 1);

if length(r) == 0
  r = randn(xlen,1);
end
r = r(1:xlen);

R = specgram(r/32768/12, NFFT, SAMPRATE, winpts, NOVERLAP);
R = R .* sqrt(y);;
x = ispecgram(R, NFFT, SAMPRATE, winpts, NOVERLAP);


function[varargout]=vrep(varargin)
%VREP  Replicates an array along a specified dimension.
%
%   Y=VREP(X,N,DIM) replicates the array by N times along dimension
%   dimension DIM.  For instance:
%
%        VREP([1:4]',3,2)=[ [1:4]' [1:4]' [1:4]' ]
%
%   This is often useful in array algebra.
%
%   IF N and DIM are arrays of length M, then X is replicated along each of
%   the M different dimensions: N(1) times along dimensions DIM(1), etc.
%
%   [Y1,Y2,...,YP]=VREP(X1,X2,...,XP,N,DIM) also works.
%
%   See also VINDEX, DIM.
%   __________________________________________________________________
%   This is part of JLAB --- type 'help jlab' for more information
%   (C) 2001--2018 J.M. Lilly --- type 'help jlab_license' for details

if strcmpi(varargin{1}, '--t')
  vrep_test,return
end

n=varargin{end-1};
ndim=varargin{end};

for i=1:length(varargin)-2
   varargout{i}=varargin{i};
end

for i=1:length(varargin)-2
    for j=1:length(n)
        varargout{i}=vrep1(varargout{i},n(j),ndim(j));
    end
end

eval(to_overwrite(nargin-2))

%You would think Matlab would provide a simpler way to do this.
function[y]=vrep1(x,n,dim)

str='y=repmat(x,[';
ndx=ndims(x);
for i=1:max(ndx,dim)
    if i~=dim
        str=[str '1,'];
    else
	str=[str 'n,'];
    end
end
str=[str(1:end-1) ']);'];
eval(str);

function[]=vrep_test

ans1=vrep((1:4)',3,2);
ans2=[ (1:4)' (1:4)' (1:4)' ];
reporttest('VREP', aresame(ans1,ans2))

x1=(1:4)';x2=(1:4)';
vrep(x1,x2,3,2);
reporttest('VREP output redirect', aresame(x1,ans1) && aresame(x2,ans2))


 function y = ifft_sym(varargin)
%function y = ifft_sym(varargin)
%|
%| matlab 7.0 introduced a 'symmetric' option to ifft to handle
%| spectra that are (circularly) hermitian symmetric (real signal).
%| this glue routine is to provide backward compatibility for matlab 6.5.
%| Caution: v7 ifft with 'symmetric' just uses the first half of the spectrum
%| along whichever dimension is requested.  Here, for pre v7, I just take
%| the real part.  The difference is neglible in the cases where this
%| routine is expected to be used, where the spectrum should be exactly
%| symmetric but has slight asymmetry due to numerical precision.
%| If the spectrum is severely asymmetric, then "real(ifft())" and
%| ifft(..., 'symmetric') will differ substantially.  (But one should
%| not call this routine in such cases.)

if ~nargin, ir_usage, end
if nargin == 1 && streq(varargin{1}, 'test'), ifft_sym_test, return, end

if ir_is_octave || is_pre_v7
	y = ifft(varargin{:});
	if isa(varargin{1}, 'double')
		tol = 1e-11;
	else
		tol = 1e-6;
	end

	y = reale(y, tol, 'prompt');
else
	y = ifft(varargin{:}, 'symmetric');
end

function y = ifft_sym_test
del = 10^5*eps;
format compact
x1 = [4 2+0i*del 8 2-1i*del]
y1 = ifft(x1)
y2 = ifft_sym(x1)
x2 = fft(y2)
y1 - y2


% VL_PEGASOS PEGASOS linear SVM solver
%   W = VL_PEGASOS(X, Y, LAMBDA) learns a linear SVM W given training
%   vectors X, their labels Y, and the regularization parameter LAMBDA
%   using the PEGASOS [1] solver. The algorithm finds a minimizer W of
%   the objective function
%
%     LAMBDA/2 |W|^2 + 1/N SUM_i LOSS(W, X(:,i), Y(i))
%
%   where LOSS(W,X,Y) = MAX(0, 1 - Y W'X) is the hinge loss and N is
%   the number of training vectors in X.
%
%   [W B INFO] = VL_SVMPEGASOS(X, Y, LAMBDA) learns a linear SVM W
%   and a bias B given training vectors X, their labels Y, and the
%   regularization parameter LAMBDA using the PEGASOS [1]
%   solver. INFO is a struct containing the input parameters plus
%   diagnostic informations:
%
%   energy::
%     SVM energy value.
%
%   iterations::
%     Number of iterations performed.
%
%   elapseTime::
%     Elapsed time since the start of the SVM learning.
%
%   regulizerTerm::
%     Value of the SVM regulizer term.
%
%   lossPos::
%     Value of loss function only for data points labeled positives.
%
%   lossNeg::
%     Value of loss function onlt for data points labeled negatives.
%
%   hardLossPos::
%     Number of mislabeled positive points.
%
%   hardLossNeg::
%     Number of mislabeled negative points.
%
%   ALGORITHM. PEGASOS is an implementation of stochastic subgradient
%   descent. At each iteration a data point is selected at random, the
%   subgradient of the cost function relative to that data point is
%   computed, and a step is taken in that direction. The step size is
%   inversely proportional to the iteration number. See [1] for
%   details.
%
%   VL_SVMPEGASOS() accepts the following options:
%
%   Epsilon:: [empty]
%     Specify the SVM stopping criterion threshold. If not
%     specified VL_SVMPEGASOS will finish when the maximum number
%     of iterations is reached. The stopping criterion is tested
%     after each ENERGYFREQ iteration.
%
%   MaxIterations:: [10 / LAMBDA]
%     Sets the maximum number of iterations.
%
%   BiasMultiplier:: [0]
%     Appends to the data X the specified scalar value B. This
%     approximates the training of a linear SVM with bias.
%
%   StartingModel:: [null vector]
%     Specify the initial value for the weight vector W.
%
%   StartingIteration:: [1]
%     Specify the iteration number to start from. The only effect
%     is to change the step size, as this is inversely proportional
%     to the iteration number.
%
%   StartingBias:: [0]
%     Specify the inital bias value.
%
%   BiasLearningRate:: [1]
%     Specify the frequency of the bias learning. The default
%     setting updates the bias at each iteration.
%
%   Permutation:: [empty]
%     Specify a permutation PERM to be used to sample the data (this
%     disables random sampling). Specifically, at the T-th iteration
%     the algorithm takes a step w.r.t. the PERM[T']-th data point,
%     where T' is T modulo the number of data samples
%     (i.e. MOD(T'-1,NUMSAMPLES)+1). PERM needs not to be
%     bijective. This allows specifying certain data points more or
%     less frequently, implicitly increasing their relative weight in
%     the error term. A common application is to balance an unbalanced
%     dataset.
%
%   DiagnosticFunction:: [empty]
%     Specify a function handle to be called every ENERGYFREQ iterations.
%
%   DiagnosticCallRef:: [empty]
%     Specify a paramater to be passed to the DIAGNOSTICFUNCTION handle.
%
%   EnergyFreq:: [100]
%     Specify how often the SVM energy is computed.
%
%   HOMKERMAP:: [empty]
%     Specify the use of an Homogeneus Kernel map for the training
%     data (See [2],[3]). The passed value N is such that a 2*N+1
%     dimensional approximated kernel map is computed. Each
%     training data point is expanded online into a vector of
%     dimension 2*N+1.
%
%   KChi2::
%     Compute the map for the Chi2 kernel.
%
%   KINTERS::
%     Compute the map for the intersection kernel.
%
%   KL1::
%     Same as KINTERS, but deprecated as the name is not fully
%     accurate.
%
%   KJS::
%     Compute the map for the JS (Jensen-Shannon) kernel.
%
%   Period:: [automatically tuned]
%     Set the period of the kernel specturm. The approximation is
%     based on periodicizing the kernel specturm. If not specified,
%     the period is automatically set based on the heuristic described
%     in [2].
%
%   Window:: [RECTANGULAR]
%     Set the window used to truncate the spectrum before The window
%     can be either RECTANGULAR or UNIFORM window. See [2] and the API
%     documentation for details.
%
%   Gamma:: [1]
%     Set the homogeneity degree of the kernel. The standard kernels
%     are 1-homogeneous, but sometimes smaller values perform better
%     in applications. See [2] for details.
%
%   Verbose::
%     Be verbose.
%
%   Example::
%     The options StartingModel and StartingIteration can be used
%     to continue training. I.e., the command
%
%       vl_twister('state',0) ;
%       w = vl_pegasos(x,y,lambda,'NumIterations',1000) ;
%
%     produces the same result as the sequence
%
%       vl_twister('state',0) ;
%       w = vl_pegasos(x,y,lambda,'NumIterations',500) ;
%       w = vl_pegasos(x,y,lambda,'NumIterations',500, ...
%                      'StartingIteration', 501, ...
%                      'StartingModel', w) ;
%
%   REFERENCES::
%     [1] S. Shalev-Shwartz, Y. Singer, N. Srebro, and
%     A. Cotter. Pegasos: Primal Estimated sub-GrAdient SOlver for
%     SVM. MBP, 2010.
%
%     [2] A. Vedaldi and A. Zisserman
%     `Efficient Additive Kernels via Explicit Feature Maps',
%     Proc. CVPR, 2010.
%
%     [3] A. Vedaldi and A. Zisserman
%     `Efficient Additive Kernels via Explicit Feature Maps',
%     PAMI, 2011 (submitted).
%
%   See also: VL_HOMKERMAP(), VL_HELP().

% AUTHORIGHTS


%GENDATW Sample dataset by given weigths
%
%   B = GENDATW(A,V,N)
%
% INPUT
%   A    Dataset
%   V    Vector with weigths for each object in A
%   N    Number of objects to be generated (default size A);
%
% OUTPUT
%   B    Dataset
%
% DESCRIPTION
% The dataset A is sampled using the weigths in V as a prior distribution.

function b = gendatw(a,v,n)

isdataset(a);
if nargin < 3, n  = size(a,1); end

v = v./sum(v);
if any(v<0)
    error('Weights should be positive');
end

mins = 0;
nn = 0;
while(mins < 2)
	N = genclass(n,v);
	L = [];
	while any(N > 0)
	    L = [L find(N > 0)];
	    N = N-1;
	end
	b = a(L,:);
	mins = min(classsizes(b));
	nn = nn + 1;
	if nn > 100
		error('Problems with weighted subsampling: classes have disappeared. Please enlarge training set.')
	end
end

return


function ARDL = ARDLmodel(ENDO,nlag,const,EXOG,nlag_ex)
% =======================================================================
% Estimate ARDL models with OLS
% =======================================================================
% ARDL = ARDLmodel(ENDO,nlag,const,EXOG,nlag_ex)
% -----------------------------------------------------------------------
% INPUT
%	- ENDO: an (nobs x 1) vector of endogenous
%	- nlag: lag length
% -----------------------------------------------------------------------
% OPTIONAL INPUT
%	- const: 0 no constant; 1 constant; 2 constant and trend; 3 constant,
%       trend, and trend^2 [dflt = 0]
%	- EXOG: optional vector of exogenous variable (nobs x 1)
%	- nlag_ex: number of lags for exogeonus variable [dflt = 0]
% -----------------------------------------------------------------------
% OUTPUT
%   - VAR: structure including VAR estimation results
%   - VARopt: structure including VAR options (see VARoption)
% =======================================================================
% VAR Toolbox 3.0
% Ambrogio Cesa-Bianchi
% ambrogiocesabianchi@gmail.com
% March 2012. Updated November 2020
% -----------------------------------------------------------------------

%% Check inputs
%===============================================
[nobs, ~] = size(ENDO);
ARDL.ENDO = ENDO;
ARDL.nlag = nlag;

% Check if ther are constant, trend, both, or none
if ~exist('const','var')
    const = 1;
end
ARDL.const = const;

% Check if there is exogenous variable
if exist('EXOG','var')
    [nobs_ex, nvar_ex] = size(EXOG);
    % Check that ENDO and EXOG are conformable
    if (nobs_ex ~= nobs)
        error('var: nobs in EXOG-matrix not the same as y-matrix');
    end
    clear nobs_ex
    % Check if there is lag order of EXOG, otherwise set it to 0
    if ~exist('nlag_ex','var')
        nlag_ex = 0;
    end
    ARDL.EXOG = EXOG;
else
    nvar_ex = 0;
    nlag_ex = 0;
    ARDL.EXOG = [];
end

%% Save some parameters and create data matrices
%===============================================
    nobse        = nobs - max(nlag,nlag_ex);
    ARDL.nobs    = nobse;
    ARDL.nlag    = nlag;
    ARDL.nlag_ex = nlag_ex;
    ncoeff       = nlag;
    ARDL.ncoeff  = ncoeff;
    ncoeff_ex    = nvar_ex + nvar_ex*nlag_ex;
    nvar         = ncoeff + ncoeff_ex + const;
    ARDL.nvar    = nvar;
    ARDL.nvar_ex = nvar_ex;
    ARDL.const   = const;

% Create independent vector and lagged dependent matrix
[Y, X] = VARmakexy(ENDO,nlag,const);

% Create (lagged) exogenous matrix
if exist('EXOG','var')
    X_EX  = VARmakelags(EXOG,nlag_ex);
    if nlag == nlag_ex
        X = [X X_EX];
    elseif nlag > nlag_ex
        diff = nlag - nlag_ex;
        X_EX = X_EX(diff+1:end,:);
        X = [X X_EX];
    elseif nlag < nlag_ex
        diff = nlag_ex - nlag;
        Y = Y(diff+1:end,:);
        X = [X(diff+1:end,:) X_EX];
    end
end

ARDL.meth = 'ols';
ARDL.y = Y;
ARDL.x = X;

% xpxi = (X'X)^(-1)
if nobse < 10000
  [~, r] = qr(X,0);
  xpxi = (r'*r)\eye(nvar);
else
  xpxi = (X'*X)\eye(nvar);
end;

% OLS estimator
beta = xpxi*(X'*Y);
ARDL.beta = beta;

% Predicted values & residuals
ARDL.yhat = X*ARDL.beta;
ARDL.resid = Y - ARDL.yhat;

% Covariance matrix of residuals
sigu = ARDL.resid'*ARDL.resid;
ARDL.sige = sigu/(nobse-nvar);

% Covariance matrix of beta
sigbeta = ARDL.sige*xpxi;
ARDL.sigbeta = sigbeta;

% Std errors of beta, t-stats, and intervals
tmp = (ARDL.sige)*(diag(xpxi));
bstd = sqrt(tmp);
ARDL.bstd = bstd;
tcrit=-tdis_inv(.025,nobse);
ARDL.bint=[ARDL.beta-tcrit.*bstd, ARDL.beta+tcrit.*bstd];
ARDL.tstat = ARDL.beta./(sqrt(tmp));
ARDL.tprob = tdis_prb(ARDL.tstat,nobs);

% R2
ym = Y - mean(Y);
rsqr1 = sigu;
rsqr2 = ym'*ym;
ARDL.rsqr = 1.0 - rsqr1/rsqr2; % r-squared
rsqr1 = rsqr1/(nobse-nvar);
rsqr2 = rsqr2/(nobse-1.0);
if rsqr2 ~= 0
    ARDL.rbar = 1 - (rsqr1/rsqr2); % rbar-squared
else
    ARDL.rbar = ARDL.rsqr;
end;

% Durbin-Watson
ediff = ARDL.resid(2:nobse) - ARDL.resid(1:nobse-1);
ARDL.dw = (ediff'*ediff)/sigu; % durbin-watson
ARDL.const = const;

% F-test
if const>0
    fx = X(:,1);
    fxpxi = (fx'*fx)\eye(1);
    fbeta = fxpxi*(fx'*Y);
    fyhat = fx*fbeta;
    fresid = Y - fyhat;
    fsigu = fresid'*fresid;
    fym = Y - mean(Y);
    frsqr1 = fsigu;
    frsqr2 = fym'*fym;
    frsqr = 1.0 - frsqr1/frsqr2; % r-squared
    ARDL.F = ((frsqr-ARDL.rsqr)/(1-nvar)) / ((1-ARDL.rsqr)/(nobse-nvar));
end

% % Long-run coefficients
% q = ncoeff_ex;
% p = ncoeff;
%
% sumendo = sum(beta(const+1:const+p)); % sum of lagged endo
% sumexog = sum(beta(const+p+1:end)); % sum of cont and lagged exog
%
% theta = sumexog/(1-sumendo);
% ARDL.theta = theta;
%
% aux1(1:p,1) = sumexog/((1-sumendo)^2);
% aux2(1:q,1) = 1/(1-sumendo);
% dtheta = [aux1; aux2];
% sigbeta_noconst = sigbeta(const+1:nvar,const+1:nvar);
% ARDL.sigtheta = dtheta'*sigbeta_noconst*dtheta;


function [graph_prop, graph_prop_glob] = bct_toolbox_undirected_graph_metrics(r, thresh, varargin)
% Calculate some Sporns BCT toolbox functions
%
% graph_prop = bct_toolbox_undirected_graph_metrics(r, [threshold_input])
%
% Inputs:
% r = correlation matrix
% thresh = 0 to 1 value. (.1 is a common value)
%
% optional:
%   'doplots' - show matrices
%   'doweighted' -- this increase compute time by several orders of
%   magnitude. Default = false
%
% Outputs:
% graph_prop = A table of node-level graph metrics
% graph_prop_glob = A table of global graph metrics
%
% For BCT toolbox, see:
% https://sites.google.com/site/bctnet/
%
% For descriptions of metrics, see:
% https://sites.google.com/site/bctnet/measures/list
%
% Notes:
% - Some metrics are calculated on thresholded, weighted (continuous valued) matrix r
%   Sig matrix or link density threshold will be applied.
%   To use without any thresholding, sig mat should be all ones, or link density 100
% - Others are calculated on thresholded, binarized matrix (positive connections only)
%   If you enter a sig matrix, this will be used as the input to graph metric functions
%   If you enter a link density, a binary matrix will be calculated
% - Fisher r to Z is computed, which will have little impact in many cases
%   but may help in some cases
% - Many BCT functions will not use negative values and ignore weights, so require thresholding.
%   P-values/statistical significance is one way to threshold.
%   Another way would be using multiple arbitrary thresholds (e.g., 10% link density)
%   The latter may be practical for individual subjects, unless individual-subject stats are calculated and saved
% - This function prioritizes
% - Degree, etc. may possibly be related to confounds (head motion), and may want to adjust/test for this.
%
% Examples:
% -------------------------------------------------------
% % start with r, a series of correlation matrices, one per subject (k x k x n_subjects)
% OUT = ttest3d(r);                                             % Get some basic stats
% graph_prop = bct_toolbox_undirected_graph_metrics(OUT.r);     % input group mean correlation matrix
%
% rr = corr(table2array(graph_prop));                           % correlate the metrics
% plot_correlation_matrix(rr, 'names', graph_prop.Properties.VariableNames)
%
% % Note: correlations among community vectors are not meaningful.
% % for similarity among communities/modules detected with different methods, see partition_distance.m

% Check path
if isempty(which('threshold_proportional.m'))
    error('The BCT toolbox does not seem to be on your Matlab path. See https://sites.google.com/site/bctnet/');
end

doplots = false;
doweighted = false;
if any(strcmp(varargin, 'doplots')), doplots = true; end
if any(strcmp(varargin, 'doweighted')), doweighted = true; end

% Prep r for BCT undirected
r = double(r);
r = (r' + r) ./ 2;          % enforce symmetry (rounding error possible)
r = r - eye(size(r));       % for BCT and squareform

% Missing regions/constant values will give NaNs, so need to account for them

numnans = any(isnan(r));
if sum(numnans) > .10 * length(numnans)
    warning('More than 10% of vars have NaN correlation values [missing data?]. Replacing NaNs with 0s. Be careful about effects on subsequent metrics');
end

r(isnan(r)) = 0;

% Threshold: Use sig matrix or link density
bu_matrix = weight_conversion(threshold_proportional(r, thresh), 'binarize');

% Make weighted matrix based on Fisher transform
z = rToZ(r);
z(isinf(z)) = max(z(~isinf(z))); % correlations of 1.00 get transformed to Inf, which causes BCT to crash in some functions. Replace w/ max value. Note that this code does not correctly handle case of r = -1.00, as this is very unlikely.
wu_matrix = z;

% view
if doplots
    create_figure('BCT networks', 1, 2)
    imagesc(bu_matrix), colorbar
    subplot(1,2,2)
    imagesc(wu_matrix), colorbar
end

% Node-level properties: Undirected binary and weighted networks
graph_prop = table();

graph_prop.community = modularity_und(bu_matrix, 1);           % C: communities from adjacency matrix
graph_prop.core_w = core_periphery_dir(wu_matrix, 1)';         % Core-periphery

graph_prop.degree = degrees_und(bu_matrix)';                   % Node degree
graph_prop.betweenness_bin = betweenness_bin(double(bu_matrix))';  % note: logical did not work in some cases...
graph_prop.clustercoef_bin = clustering_coef_bu(bu_matrix);        % Clustering coefficient
graph_prop.local_efficiency_bin = efficiency_bin(bu_matrix, 1);    % Local efficiency

% weighted matrix computations
if doweighted
    graph_prop.strength = strengths_und(wu_matrix)';               % weighted strength. tested: same results as using r_mean
    graph_prop.local_efficiency_weighted = efficiency_wei(wu_matrix, 1);    % Local efficiency
    graph_prop.eigenvector_centrality = eigenvector_centrality_und(wu_matrix);    % Eigenvector centrality (similar to PageRank)
    graph_prop.clustercoef_weighted = clustering_coef_wu(wu_matrix);        % Clustering coefficient
    graph_prop.betweenness_weighted = betweenness_wei(wu_matrix);  % note: slow

    graph_prop.community_w = modularity_und(wu_matrix, 1);
    graph_prop.community_w2 = community_louvain(wu_matrix, 1, [], 'negative_asym');
end
    % Others to consider
% assortativity_bin(bu_network, 0);
% kcore_bu
% graph_prop.betweenness_w = betweenness_wei(OUT.r);            % slow
% graph_prop.rich_club_coeff = rich_club_bu(bu_network)';       % output len not same as number of nodes

% Note: for similarity among communities/modules, see partition_distance.m

% Global properties
graph_prop_glob = table();

graph_prop_glob.trans_bu = transitivity_bu(bu_matrix);
graph_prop_glob.glob_efficiency = efficiency_bin(bu_matrix);

end


%DXDEMO Demonstrate distance transform planner using animation
%
% MORPHDEMO(IM, SE, OPTIONS) displays an animation to show the principles
% of the mathematical morphology operations dilation or erosion.  Two
% windows are displayed side by side, input binary image on the left and
% output image on the right.  The structuring element moves over the input
% image and is colored red if the result is zero, else blue.  Pixels in
% the output image are initially all grey but change to black or white
% as the structuring element moves.
%
% OUT = MORPHDEMO(IM, SE, OPTIONS) as above but returns the output image.
%
% Options::
% 'dilate'      Perform morphological dilation
% 'erode'       Perform morphological erosion
% 'delay'       Time between animation frames (default 0.5s)
% 'scale',S     Scale factor for output image (default 64)
% 'movie',M     Write image frames to the folder M
%
% Notes::
% - This is meant for small images, say 10x10 pixels.
%
% See also IMORPH, IDILATE, IERODE.

function out = dxdemo(map, goal, varargin)

    opt.delay = 0;
    opt.movie = [];
    opt.scale = 64;
    opt.metric = {'euclidean', 'manhattan'};
    opt = tb_optparse(opt, varargin);

    set(gcf, 'Position', [90         435        1355         520])
    clf

    goal = [4 8];
    start = [7 2];
    opt.metric = 'euclidean';
    opt.movie = [];%'dxform2.mp4'
    opt.delay = 0;

    % make a simple map
    occgrid = zeros(10,10);
    occgrid(4:6,3:7) = 1;
    %occgrid(7:8,7) = 1;  % extra bit

    cost0 = occgrid;
    cost0(cost0==1) = NaN;

    cost = cost0;
    cost(cost0==0) = Inf;
    cost(goal(2), goal(1)) = 0;

    if ~isempty(opt.movie)
        anim = Animate(opt.movie);
    end

    if ~isempty(opt.movie)
        anim.add();
    end

    switch opt.metric
        case 'cityblock'
            m = [inf 1 inf
                1  0  1
                inf 1 inf];
        case 'euclidean'
            r2 = sqrt(2);
            m = [r2 1 r2
                1 0  1
                r2 1 r2];
        otherwise
            error('unknown distance metric');
    end

    iteration = 0;
    ninf = 0;
    n2 = 1; % half width
    n22 = 1.5;

    newcost = inf(size(cost));

    title('Wavefront path planning simulation');

    while true
        iteration = iteration+1;

        maxval = max(max(cost(isfinite(cost))));

        subplot(121)
        showpixels(cost, 'contrast', maxval*0.7, 'fmt', '%.2g', 'cscale', [0 maxval+2], 'fontsize', 20, 'nancolor', 'nohideinf', 'infsymbol', 'nohidenan', 'infcolor')
        xlabel('x', 'FontSize', 20); ylabel('y', 'FontSize', 20);
            hpatch1 = patch(1, 1,  'y', 'FaceAlpha', 0.5);

        for r=n2+1:numrows(cost)-n2
            for c=n2+1:numcols(cost)-n2

                win = cost(r-n2:r+n2, c-n2:c+n2);

                if isnan(cost(r,c))
                    newcost(r,c) = NaN;
                else
                    newcost(r,c) = min(min(win+m));
                end

                % animate the patch
                hpatch1.XData = [c-n22 c+n22 c+n22 c-n22];
                hpatch1.YData = [r-n22 r-n22 r+n22 r+n22];

                subplot(122)
                cla
                showpixels(newcost, 'contrast', maxval*0.7, 'fmt', '%.2g', 'cscale', [0 maxval+2], 'fontsize', 20, 'nancolor', 'nohideinf', 'infsymbol', 'nohidenan', 'infcolor')
                xlabel('x', 'FontSize', 20); ylabel('y', 'FontSize', 20)
                    hpatch2 = patch([c-0.5 c+0.5 c+0.5 c-0.5], [r-0.5 r-0.5 r+0.5 r+0.5],  'y', 'FaceAlpha', 0.5);

                if ~isempty(opt.movie)
                    anim.add();
                end
                if opt.delay == 0
                    drawnow
                else
                    pause(opt.delay);
                end
            end
        end

        cost = newcost;
        ninfnow = sum(sum( isinf(cost(2:end-1,2:end-1)) )) % current number of Infs
        if ninfnow == 0 || ninfnow == ninf
            % stop if the number of Infs left in the map had stopped reducing
            % it may never get to zero if there are unreachable cells in the map
            break;
        end
        ninf = ninfnow;

    if ~isempty(opt.movie)
        anim.close();
    end
end


function [opttheta] = minFuncSGD(funObj,theta,data,labels,...
                        options)
% Runs stochastic gradient descent with momentum to optimize the
% parameters for the given objective.
%
% Parameters:
%  funObj     -  function handle which accepts as input theta,
%                data, labels and returns cost and gradient w.r.t
%                to theta.
%  theta      -  unrolled parameter vector
%  data       -  stores data in m x n x numExamples tensor
%  labels     -  corresponding labels in numExamples x 1 vector
%  options    -  struct to store specific options for optimization
%
% Returns:
%  opttheta   -  optimized parameter vector
%
% Options (* required)
%  epochs*     - number of epochs through data
%  alpha*      - initial learning rate
%  minibatch*  - size of minibatch
%  momentum    - momentum constant, defualts to 0.9

%%======================================================================
%% Setup
assert(all(isfield(options,{'epochs','alpha','minibatch'})),...
        'Some options not defined');
if ~isfield(options,'momentum')
    options.momentum = 0.9;
end;
epochs = options.epochs;
alpha = options.alpha;
minibatch = options.minibatch;
m = length(labels); % training set size
% Setup for momentum
mom = 0.5;
momIncrease = 20;
velocity = zeros(size(theta));

%%======================================================================
%% SGD loop
it = 0;
for e = 1:epochs

    % randomly permute indices of data for quick minibatch sampling
    rp = randperm(m);

    for s=1:minibatch:(m-minibatch+1)
        it = it + 1;

        % increase momentum after momIncrease iterations
        if it == momIncrease
            mom = options.momentum;
        end;

        % get next randomly selected minibatch
        mb_data = data(:,:,:,rp(s:s+minibatch-1));
        mb_labels = labels(rp(s:s+minibatch-1));

        % evaluate the objective function on the next minibatch
        [cost grad] = funObj(theta,mb_data,mb_labels);

        % Instructions: Add in the weighted velocity vector to the
        % gradient evaluated above scaled by the learning rate.
        % Then update the current weights theta according to the
        % sgd update rule

        %%% YOUR CODE HERE %%%
        velocity = mom * velocity + alpha * grad;
        theta = theta - velocity;

        fprintf('Epoch %d: Cost on iteration %d is %f\n',e,it,cost);
    end;

    % aneal learning rate by factor of two after each epoch
    alpha = alpha/2.0;

end;

opttheta = theta;

end


function test_failed=test_realout
%TEST_REALOUT  Test if functions produce real-valued output

  test_failed=0;

  disp(' ===============  TEST_REALOUT ================');

  a = 7;
  M = 19;
  W = 3;
  L = a*M*4;
  Nwil = L/(2*M);
  Nmd  = L/M;
  Ngab = L/a;

  Nfft = 19;

  test_failed=realhelper(test_failed,'dwilt',randn(L,W),randn(L,1),M);
  test_failed=realhelper(test_failed,'dwilt',randn(L,W),randn(2*M,1),M);

  test_failed=realhelper(test_failed,'wmdct',randn(L,W),randn(L,1),M);
  test_failed=realhelper(test_failed,'wmdct',randn(L,W),randn(2*M,1),M);

  test_failed=realhelper(test_failed,'idwilt',randn(2*M,Nwil),randn(L,1));
  test_failed=realhelper(test_failed,'idwilt',randn(2*M,Nwil),randn(2*M,1));

  test_failed=realhelper(test_failed,'iwmdct',randn(M,Nmd),randn(L,1));
  test_failed=realhelper(test_failed,'iwmdct',randn(M,Nmd),randn(2*M,1));

  test_failed=realhelper(test_failed,'gabdual',randn(L,1),a,M);
  test_failed=realhelper(test_failed,'gabdual',randn(L,1),a,M);
  test_failed=realhelper(test_failed,'gabtight',randn(M,1),a,M);
  test_failed=realhelper(test_failed,'gabtight',randn(M,1),a,M);

  test_failed=realhelper(test_failed,'dcti',randn(Nfft,1));
  test_failed=realhelper(test_failed,'dctii',randn(Nfft,1));
  test_failed=realhelper(test_failed,'dctiii',randn(Nfft,1));
  test_failed=realhelper(test_failed,'dctiv',randn(Nfft,1));
  test_failed=realhelper(test_failed,'dsti',randn(Nfft,1));
  test_failed=realhelper(test_failed,'dstii',randn(Nfft,1));
  test_failed=realhelper(test_failed,'dstiii',randn(Nfft,1));
  test_failed=realhelper(test_failed,'dstiv',randn(Nfft,1));

  test_failed=realhelper(test_failed,'pfilt',randn(L,1),randn(L,1));

  c=dgtreal(randn(L,W),randn(L,1),a,M);
  test_failed=realhelper(test_failed,'idgtreal',c,randn(L,1),a,M);
  test_failed=realhelper(test_failed,'idgtreal',c,randn(M,1),a,M);

  c=fftreal(randn(Nfft,1));
  test_failed=realhelper(test_failed,'ifftreal',c,Nfft);

function test_failed=realhelper(test_failed,funname,varargin)

  outres=feval(funname,varargin{:});
  res=~isreal(outres);

  if res>0
      outres
  end;
  [test_failed,fail]=ltfatdiditfail(res,test_failed);

  fprintf('REAL %s %i %s\n',funname,res,fail);


function a = i4mat_elim ( m, n, a )

%*****************************************************************************80
%
%% I4MAT_ELIM carries out exact Gauss elimination on an I4MAT.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 April 2005
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, the number of rows in A.
%
%    Input, integer N, the number of columns in A.
%
%    Input, integer A(M,N), the matrix to be Gauss eliminated.
%
%    Output, integer A(M,N), the Gauss-eliminated matrix.
%

%
%  Initialize the swap parity counter.
%
  iswap = 1;
%
%  For each column JCOL...
%
  for jcol = 1 : min ( m, n )
%
%  Find the maximum element in rows JCOL through M.
%
    amax = abs ( a(jcol,jcol) );
    imax = jcol;

    for i = jcol + 1 : m
      if ( amax < abs ( a(i,jcol) ) )
        amax = abs ( a(i,jcol) );
        imax = i;
      end
    end
%
%  If the maximum entry is nonzero, then...
%
    if ( amax ~= 0 )
%
%  If the maximum entry does not occur in row JCOL, then swap rows.
%
      if ( imax ~= jcol )
        iswap = -iswap;
        temp(1:n) = a(jcol,1:n);
        a(jcol,1:n) = a(imax,1:n);
        a(imax,1:n) = temp(1:n);
      end
%
%  Eliminate all nonzero entries in column JCOL, below the diagonal entry.
%
      for i = jcol+1 : m

        if ( a(i,jcol) ~= 0 )

          jmult = a(i,jcol);
          imult = a(jcol,jcol);
          ifact = i4_gcd ( imult, jmult );
          imult = imult / ifact;
          jmult = jmult / ifact;

          for j = jcol : n
            a(i,j) = jmult * a(jcol,j) - imult * a(i,j);
          end

        end

      end
%
%  Remove any row or column factors.
%
      [ a, irow, icol ] = i4mat_red ( m, n, a );

    end

  end

  return
end


function [prob,sol,fmin] = milp_prob(varargin)
%MILP_PROB  Return an OPTI MILP
%
%   prob = milp_prob(no) return a pre-built optiprob of a saved MILP.
%
%   [prob,sol,fmin] = milp_prob(no) returns the optimum solution and
%   function eval at the optimum
%
%   no = milp_prob() returns the number of problems available for testing.

%   (C) 2011 Jonathan Currie (I2C2)

%Check if just returning no problems
if(nargin < 1)
    prob = 10; sol = []; fmin = [];
    return;
else
    no = varargin{1};
end

%Big switch yard
switch(no)
    case 1
        f = -[-1, 2]';
        A = [2, 1;-4, 4];
        b = [5, 5]';
        e = -[1, 1];
        xint = 'II';
        prob = optiprob('f',f,'mix',A,b,e,'int',xint);
        sol = [1;2];
        fmin = -3;

    case 2
        f = -[50, 100];
        A = [10, 5;4, 10; 1, 1.5];
        b = [2500, 2000, 450]';
        e = [-1, -1, -1];
        xint = 'II';
        prob = optiprob('f',f,'mix',A,b,e,'int',xint);
        sol = [187;125];
        fmin = -21850;

    case 3
        f = [40, 36];
        A = [5, 3];
        b = 45;
        e = 1;
        ub = [8, 10];
        xint = 'II';
        prob = optiprob('f',f,'mix',A,b,e,'ub',ub,'int',xint);
        sol = [8;2];
        fmin = 392;

    case 4
        f = [3, -7, -12];
        A = [-3, 6, 8;6, -3, 7;-6, 3, 3];
        b = [12, 8, 5]';
        e = [-1, -1, -1];
        xint = 'III';
        prob = optiprob('f',f,'mix',A,b,e,'int',xint);
        sol = [2;3;0];
        fmin = -15;

    case 5
        f = [2, 3, 7, 7];
        A = [1, 1, -2, -5;-1, 2, 1, 4];
        b = [2, 3]';
        e = [1, 1];
        lb = zeros(4,1);
        ub = [30 100 20 1]';
        xint = 'CICI';
        prob = optiprob('f',f,'mix',A,b,e,'bounds',lb,ub,'int',xint);
        sol = [0;2;0;0];
        fmin = 6;

    case 6
        f = [1, 2, 3, 7, 8, 8];
        A = [5, -3, 2, -3, -1, 2; -1, 0, 2, 1, 3, -3;1, 2, -1, 0, 5, -1];
        b = [-5, -1, 3]';
        e = [1, 1, 1];
        lb = zeros(6,1);
        ub = 10*ones(6,1);
        xint = 'IIIIII';
        prob = optiprob('f',f,'mix',A,b,e,'bounds',lb,ub,'int',xint);
        sol = [1;1;0;0;0;0];
        fmin = 3;

    case 7
        n = 40;
        t = (0:n-1)';
        y = 3.5 -.2*t;
        b = y + 0.5*ones(size(y));
        m = [ones(n,1),t(:)];
        A = [m,-m,eye(n)];
        f = [sum(m),sum(-m),2*ones(1,n)];
        e = ones(n,1);
        lb = zeros(n+4,1);
        ub = [10, 10, 10, 10, 5*ones(1,n)];
        xint = [1 3];
        prob = optiprob('f',f,'mix',A,b,e,'bounds',lb,ub,'int',xint);
        sol = [4;0;0;0.2;zeros(40,1)];
        fmin = 4.0000000000000044;

    case 8
        f = -[8, 15];
        A = [10, 21;2, 1];
        b = [156, 22]';
        e = [-1, -1];
        xint = 'IC';
        prob = optiprob('f',f,'mix',A,b,e,'int',xint);
        sol = [9;3.14285714285714];
        fmin = -119.14285714285714;

    case 9
        f = -[3, 13];
        A = [2, 9;11, -8];
        b = [40, 82]';
        e = [-1, -1];
        xint = 'IC';
        prob = optiprob('f',f,'mix',A,b,e,'int',xint);
        sol = [9;2.44444444444444];
        fmin = -58.77777777777778;

    case 10
        f = -[592, 381, 273, 55, 48, 37, 23];
        A = [3534, 2356, 1767, 589, 528, 451, 304];
        b = 119567;
        e = -1;
        lb = zeros(7,1);
        ub = [100 50 33 20 77 44 20]';
        xint = 'IIIIIII';
        prob = optiprob('f',f,'mix',A,b,e,'bounds',lb,ub,'int',xint);
        sol = [32;2;1;0;0;0;0];
        fmin = -19979;

    otherwise
        error('Problem not available or not implemented yet');
end


% Make an HMM with Gaussian observations
%   X1 -> X2
%   |     |
%   v     v
%   Y1    Y2

intra = zeros(2);
intra(1,2) = 1;
inter = zeros(2);
inter(1,1) = 1;
n = 2;

Q = 2; % num hidden states
O = 2; % size of observed vector
ns = [Q O];
bnet = mk_dbn(intra, inter, ns, 'discrete', 1, 'observed', 2);

prior0 = normalise(rand(Q,1));
transmat0 = mk_stochastic(rand(Q,Q));
mu0 = rand(O,Q);
Sigma0 = repmat(eye(O), [1 1 Q]);
bnet.CPD{1} = tabular_CPD(bnet, 1, prior0);
%% we set the cov prior to 0 to give same results as HMM toolbox
%bnet.CPD{2} = gaussian_CPD(bnet, 2, 'mean', mu0, 'cov', Sigma0, 'cov_prior_weight', 0);
bnet.CPD{2} = gaussian_CPD(bnet, 2, 'mean', mu0, 'cov', Sigma0);
bnet.CPD{3} = tabular_CPD(bnet, 3, transmat0);

T = 5; % fixed length sequences

engine = {};
engine{end+1} = smoother_engine(jtree_2TBN_inf_engine(bnet));
engine{end+1} = smoother_engine(hmm_2TBN_inf_engine(bnet));
engine{end+1} = hmm_inf_engine(bnet);
engine{end+1} = jtree_unrolled_dbn_inf_engine(bnet, T);
%engine{end+1} = frontier_inf_engine(bnet);
engine{end+1} = bk_inf_engine(bnet, 'clusters', {[1]});
engine{end+1} = jtree_dbn_inf_engine(bnet);

inf_time = cmp_inference_dbn(bnet, engine, T);

ncases = 2;
max_iter = 2;
[learning_time, CPD, LL, cases] = cmp_learning_dbn(bnet, engine, T, 'ncases', ncases, 'max_iter', max_iter);

% Compare to HMM toolbox

data = zeros(O, T, ncases);
for i=1:ncases
  data(:,:,i) = cell2num(cases{i}(bnet.observed, :));
end

tic
[LL2, prior2, transmat2, mu2, Sigma2] = mhmm_em(data, prior0, transmat0, mu0, Sigma0, [],  'max_iter', max_iter);
t=toc;
disp(['HMM toolbox took ' num2str(t) ' seconds '])

e = 1;
assert(approxeq(prior2, CPD{e,1}.CPT))
assert(approxeq(mu2, CPD{e,2}.mean))
assert(approxeq(Sigma2, CPD{e,2}.cov))
assert(approxeq(transmat2, CPD{e,3}.CPT))
assert(approxeq(LL2, LL{e}))


% Load, modify and export a fig file as an eps file.

clear all;
addpath('../lib');

%% lets plot 3 cycles of 50Hz AC voltage
f = 50;  % frequency
Vm = 10; % peak
phi = 0; % phase

% generate the signal
t = [0:0.0001:3/f];
th = 2*pi*f*t;
v = Vm*sin(th+phi);

% plot it
figure;
plot(t*1E3, v);

% change settings
opt = [];
opt.XLabel = 'Time, t (ms)'; % xlabel
opt.YLabel = 'Voltage, V (V)'; %ylabel
opt.Title = 'Voltage as a function of time';

% Save? comment the following line if you do not want to save
opt.FileName = 'plotSimple1.eps';

% apply the settings
setPlotProp(opt);


%A 2D uniform PDF function.
%xrange and yrange are inclusive limits of region
%z is a 2D sample point.
function val = unifpdf_2d(xrange, yrange, z)
    minX = xrange(1);
    maxX = xrange(2);
    minY = yrange(1);
    maxY = yrange(2);
    evalX = z(1);
    evalY = z(2);
    if(evalX < minX)
        val = 0;
        return;
    elseif(evalX > maxX)
        val = 0;
        return
    elseif (evalY < minY)
        val = 0;
        return;
    elseif(evalY > maxY)
        val = 0;
        return;
    else
        val = 1 / ((maxX - minX) * (maxY - minY));
    end

end


% Analytic SME meanamp calc snippet
% axs Dec 2018
% ams Jan 2023, includes corrected SME
function [outdata] = sme_analytic(EEG, epoch_list, dq_window_times)

sizes = size(EEG.data);

assert(numel(sizes)==3, 'sme_analytic needs bin-epoched EEG data');

if exist('dq_window_times','var') == 0
    dq_window_times = [];
end

% if there are 3 cols to win_times, use last 2
if size(dq_window_times,2) == 3
    dq_window_times = dq_window_times(:,2:3);
end

n_bins = length(epoch_list);
n_elec = sizes(1);
n_times = sizes(2);
n_beps_total = sizes(3);

if isempty(dq_window_times)
    n_windows = 1;
    win_times_starts = 1;
    win_times_ends = EEG.times(end);
else
    n_windows = size(dq_window_times,1);
    win_times_starts = dq_window_times(:,1);
    win_times_ends = dq_window_times(:,2);
end

% Convert times from ms to datapoint idx
win_dps_starts = zeros(1,n_windows);
win_dps_ends = zeros(1,n_windows);
for t = 1:n_windows
    win_dps_starts(t) = find(abs(EEG.times-win_times_starts(t))==min(abs(EEG.times-win_times_starts(t))),1);
    win_dps_ends(t) = find(abs(EEG.times-win_times_ends(t))==min(abs(EEG.times-win_times_ends(t))),1);
end

for b = 1:n_bins
    n_beps_per_bin(b) = numel(epoch_list(b).good_bep_indx);
end

SME_out = zeros(n_elec,n_windows,n_bins);
SME_unbias_out = zeros(n_elec,n_windows,n_bins);

for b = 1:n_bins        % for each bin
    for t = 1:n_windows % and each SME time window

    data_here = EEG.data(1:n_elec,win_dps_starts(t):win_dps_ends(t),epoch_list(b).good_bep_indx);
    window_mean = squeeze(mean(data_here,2));

    %SD (N-1)
    SME_sd = std(window_mean,0,2);
    %SME_bias = std(window_mean,0,2) / sqrt(n_beps_per_bin(b));
    SME_bias = SME_sd / sqrt(n_beps_per_bin(b));
    SME_out(1:n_elec,t,b) = SME_bias;

    %Unbiased SD (Gurland & Tripathi)
    nTimes = size(window_mean,2);
    SME_unbias = (((SME_sd * sqrt((nTimes-1)))/ sqrt((nTimes-(3/2)+(1/(8*(nTimes-1)))))) / sqrt(n_beps_per_bin(b)));
    SME_unbias_out(1:n_elec,t,b) = SME_unbias;

    end
end
%combine SME structs
outdata = struct();
outdata.SME = SME_out;
outdata.SME_corr = SME_unbias_out;


function plane = createPlane(varargin)
%CREATEPLANE Create a plane in parametrized form.
%
%   PLANE = createPlane(P1, P2, P3)
%   creates a plane containing the 3 points
%
%   PLANE = createPlane(PTS)
%   The 3 points are packed into a single 3x3 array.
%
%   PLANE = createPlane(P0, N);
%   Creates a plane from a point and from a normal to the plane. The
%   parameter N is given either as a 3D vector (1-by-3 row vector), or as
%   [THETA PHI], where THETA is the colatitute (angle with the vertical
%   axis) and PHI is angle with Ox axis, counted counter-clockwise (both
%   given in radians).
%
%   PLANE = createPlane(P0, Dip, DipDir);
%   Creates a plane from a point and from a dip and dip direction angles
%   of the plane. Parameters Dip and DipDir angles are given as numbers.
%   Dip : maximum inclination to the horizontal.
%   DipDir : direction of the horizontal trace of the line of dip,
%            measured clockwise from north.
%
%   The created plane data has the following format:
%   PLANE = [X0 Y0 Z0  DX1 DY1 DZ1  DX2 DY2 DZ2], with
%   - (X0, Y0, Z0) is a point belonging to the plane
%   - (DX1, DY1, DZ1) is a first direction vector
%   - (DX2, DY2, DZ2) is a second direction vector
%   The 2 direction vectors are normalized and orthogonal.
%
%   See also
%   planes3d, medianPlane

% ------
% Author: David Legland
% E-mail: david.legland@inrae.fr
% Created: 2005-02-18
% Copyright 2005-2022 INRA - TPV URPOI - BIA IMASTE

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

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

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

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

elseif length(varargin) == 2
    % plane origin
    p0 = varargin{1};

    % second parameter is either a 3D vector or a 3D angle (2 params)
    var = varargin{2};
    if size(var, 2) == 2
        % normal is given in spherical coordinates
        n = sph2cart2([var ones(size(var, 1))]);
    elseif size(var, 2)==3
        % normal is given by a 3D vector
        n = normalizeVector3d(var);
    else
        error ('wrong number of parameters in createPlane');
    end

    % ensure same dimension for parameters
    if size(p0, 1)==1
        p0 = repmat(p0, [size(n, 1) 1]);
    end
    if size(n, 1)==1
        n = repmat(n, [size(p0, 1) 1]);
    end

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

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

    % concatenate result in the array representing the plane
    plane = [p0 v1 v2];
    return;

elseif length(varargin)==3
    var1 = varargin{1};
    var2 = varargin{2};
    var3 = varargin{3};

    if size(var1, 2) == 3 && size(var2, 2) == 3 && size(var3, 2) == 3
        p1 = var1;
        p2 = var2;
        p3 = var3;

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

        plane = normalizePlane([p1 v1 v2]);
        return;
    elseif size(var1, 2) == 3 && size(var2, 2) == 1 && size(var3, 2) == 1
        p0 = var1;
        n = [sin(var2)*sin(var3) sin(var2)*cos(var3) cos(var2)];

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

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

        % concatenate result in the array representing the plane
        plane = [p0 v1 v2];
        return;
    else
        error('Wrong argument in "createPlane".');
    end
else
    error('Wrong number of arguments in "createPlane".');
end


function x = c8vec_sort_a2 ( n, x )

%*****************************************************************************80
%
%% C8VEC_SORT_A2 ascending sorts a complex array by L2 norm.
%
%  Discussion:
%
%    The L2 norm of A+Bi is sqrt ( A**2 + B**2 ).
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    20 March 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, length of input array.
%
%    Input, complex X(N), an unsorted array.
%
%    Output, complex X(N), a sorted copy of the array.
%
  indx = 0;
  isgn = 0;

  while ( 1 )

    [ indx, i, j ] = sort_heap_external ( n, indx, isgn );

    if ( 0 < indx )

      t = x(i);
      x(i) = x(j);
      x(j) = t;

    elseif ( indx < 0 )

      if ( c8_le_l2 ( x(i), x(j) ) )
        isgn = -1;
      else
        isgn = +1;
      end

    elseif ( indx == 0 )

      break;

    end

  end

  return
end


classdef ShapeContextDistanceExtractor < handle
    %SHAPECONTEXTDISTANCEEXTRACTOR  Implementation of the Shape Context descriptor and matching algorithm
    %
    % Proposed by [Belongie2002]. This implementation is packaged in a generic
    % scheme, in order to allow you the implementation of the common variations
    % of the original pipeline.
    %
    % ## References
    % [Belongie2002]:
    % > Belongie et al. "Shape Matching and Object Recognition using Shape
    % > Contexts" (PAMI 2002)
    %
    % See also: cv.ShapeContextDistanceExtractor.ShapeContextDistanceExtractor,
    %  cv.HausdorffDistanceExtractor, cv.matchShapes
    %

    properties (SetAccess = private)
        % Object ID
        id
    end

    properties (Dependent)
        % The number of angular bins in the shape context descriptor.
        %
        % The number of angular bins for the Shape Context Descriptor used in
        % the shape matching pipeline. default 12
        AngularBins
        % The number of radial bins in the shape context descriptor.
        %
        % The number of radial bins for the Shape Context Descriptor used in
        % the shape matching pipeline. default 4
        RadialBins
        % The inner radius of the shape context descriptor.
        %
        % default 0.2
        InnerRadius
        % The outer radius of the shape context descriptor.
        %
        % default 2
        OuterRadius
        % default false
        RotationInvariant
        % The weight of the shape context distance in the final value of the
        % shape distance.
        %
        % The shape context distance between two shapes is defined as the
        % symmetric sum of shape context matching costs over best matching
        % points. The final value of the shape distance is a user-defined
        % linear combination of the shape context distance, an image
        % appearance distance, and a bending energy. default 1.0
        ShapeContextWeight
        % The weight of the image appearance cost in the final value of the
        % shape distance.
        %
        % The image appearance cost is defined as the sum of squared brightness
        % differences in Gaussian windows around corresponding image points.
        % The final value of the shape distance is a user-defined linear
        % combination of the shape context distance, an image appearance
        % distance, and a bending energy. If this value is set to a number
        % different from 0, is mandatory to set the images that correspond to
        % each shape. default 0.0
        ImageAppearanceWeight
        % The weight of the Bending Energy in the final distance value.
        %
        % The bending energy definition depends on what transformation is being
        % used to align the shapes. The final value of the shape distance is a
        % user-defined linear combination of the shape context distance, an
        % image appearance distance, and a bending energy. default 0.3
        BendingEnergyWeight
        % default 3
        Iterations
        % The standard deviation for the Gaussian window for the image
        % appearance cost.
        %
        % default 10.0
        StdDev
    end

    %% ShapeContextDistanceExtractor
    methods
        function this = ShapeContextDistanceExtractor(varargin)
            %SHAPECONTEXTDISTANCEEXTRACTOR  Constructor
            %
            %     obj = cv.ShapeContextDistanceExtractor()
            %     obj = cv.ShapeContextDistanceExtractor('OptionName',optionValue, ...)
            %
            % ## Options
            % * __AngularBins__ see
            %   cv.ShapeContextDistanceExtractor.AngularBins, default 12
            % * __RadialBins__ see
            %   cv.ShapeContextDistanceExtractor.RadialBins, default 4
            % * __InnerRadius__ see
            %   cv.ShapeContextDistanceExtractor.InnerRadius, default 0.2
            % * __OuterRadius__ see
            %   cv.ShapeContextDistanceExtractor.OuterRadius, default 2
            % * __Iterations__ see
            %   cv.ShapeContextDistanceExtractor.Iterations, default 3
            % * __CostExtractor__ an algorithm that defines the cost matrix
            %   between descriptors, specified as
            %   `{comparerType, 'OptionName',optionValue, ...}`. See
            %   cv.ShapeContextDistanceExtractor.setCostExtractor, where
            %   `comparerType` is one of:
            %   * __NormHistogramCostExtractor__
            %   * __EMDHistogramCostExtractor__
            %   * __ChiHistogramCostExtractor__ (default)
            %   * __EMDL1HistogramCostExtractor__
            % * __TransformAlgorithm__ an algorithm that defines the aligning
            %   transformation, specified as
            %   `{transformerType, 'OptionName',optionValue, ...}`. See
            %   cv.ShapeContextDistanceExtractor.setTransformAlgorithm, where
            %   `transformerType` is one of:
            %   * __ThinPlateSplineShapeTransformer__ (default)
            %   * __AffineTransformer__
            %
            % See also: cv.ShapeContextDistanceExtractor.computeDistance
            %
            this.id = ShapeContextDistanceExtractor_(0, 'new', varargin{:});
        end

        function delete(this)
            %DELETE  Destructor
            %
            %     obj.delete()
            %
            % See also: cv.ShapeContextDistanceExtractor
            %
            if isempty(this.id), return; end
            ShapeContextDistanceExtractor_(this.id, 'delete');
        end

        function setImages(this, image1, image2)
            %SETIMAGES  Set the images that correspond to each shape, used in the calculation of the Image Appearance cost
            %
            %     obj.setImages(image1, image2)
            %
            % ## Input
            % * __image1__ Image corresponding to the shape defined by
            %   `contours1`.
            % * __image2__ Image corresponding to the shape defined by
            %   `contours2`.
            %
            % See also: cv.ShapeContextDistanceExtractor.getImages
            %
            ShapeContextDistanceExtractor_(this.id, 'setImages', image1, image2);
        end

        function setCostExtractor(this, comparerType, varargin)
            %SETCOSTEXTRACTOR  Set the algorithm used for building the shape context descriptor cost matrix
            %
            %     obj.setCostExtractor(comparerType)
            %     obj.setCostExtractor(comparerType, 'OptionName',optionValue,...)
            %
            % ## Input
            % * __comparerType__ an algorithm that defines the cost matrix
            %   between descriptors. One of:
            %   * __NormHistogramCostExtractor__ A norm based cost extraction.
            %     See cv.norm
            %   * __EMDHistogramCostExtractor__ An EMD based cost extraction.
            %     See cv.EMD
            %   * __ChiHistogramCostExtractor__ An Chi based cost extraction.
            %   * __EMDL1HistogramCostExtractor__ An EMD-L1 based cost
            %     extraction. See cv.EMDL1
            %
            % ## Options
            % The following are options accepted by all algorithms:
            %
            % * __NDummies__ default 25
            % * __DefaultCost__ default 0.2
            %
            % The following are options for the various algorithms:
            %
            % ### `NormHistogramCostExtractor`, `EMDHistogramCostExtractor`
            % * __NormFlag__ default 'L2'. This parameter matches the
            %   `NormType` and `DistType` flags of cv.norm and cv.EMD
            %   respectively.
            %
            % See also: cv.ShapeContextDistanceExtractor.getCostExtractor
            %
            ShapeContextDistanceExtractor_(this.id, 'setCostExtractor', comparerType, varargin{:});
        end

        function setTransformAlgorithm(this, transformerType, varargin)
            %SETTRANSFORMALGORITHM  Set the algorithm used for aligning the shapes
            %
            %     obj.setTransformAlgorithm(transformerType)
            %     obj.setTransformAlgorithm(transformerType, 'OptionName',optionValue,...)
            %
            % ## Input
            % * __transformerType__ an algorithm that defines the aligning
            %   transformation. One of:
            %   * __ThinPlateSplineShapeTransformer__ Definition of the
            %     transformation occupied in the paper [Bookstein89].
            %   * __AffineTransformer__ Wrapper class for the OpenCV Affine
            %     Transformation algorithm. See cv.estimateRigidTransform
            %
            % ## Options
            % The following are options for the various algorithms:
            %
            % ### `ThinPlateSplineShapeTransformer`
            % * __RegularizationParameter__ The regularization parameter for
            %   relaxing the exact interpolation requirements of the TPS
            %   algorithm. default 0
            %
            % ### `AffineTransformer`
            % * __FullAffine__ see cv.estimateRigidTransform, default true
            %
            % ## References
            % [Bookstein89]:
            % > "Principal Warps: Thin-Plate Splines and Decomposition of
            % > Deformations", by F.L. Bookstein (PAMI 1989)
            %
            % See also: cv.ShapeContextDistanceExtractor.getTransformAlgorithm
            %
            ShapeContextDistanceExtractor_(this.id, 'setTransformAlgorithm', transformerType, varargin{:});
        end

        function [image1, image2] = getImages(this)
            %GETIMAGES  Get the images that correspond to each shape, used in the calculation of the Image Appearance cost
            %
            %     [image1, image2] = obj.getImages()
            %
            % ## Output
            % * __image1__ Image corresponding to the shape defined by
            %   `contours1`.
            % * __image2__ Image corresponding to the shape defined by
            %   `contours2`.
            %
            % See also: cv.ShapeContextDistanceExtractor.setImages
            %
            [image1, image2] = ShapeContextDistanceExtractor_(this.id, 'getImages');
        end

        function value = getCostExtractor(this)
            %GETCOSTEXTRACTOR  Get the current algorithm used for building the shape context descriptor cost matrix
            %
            %     value = obj.getCostExtractor()
            %
            % ## Output
            % * __value__ output scalar struct
            %
            % See also: cv.ShapeContextDistanceExtractor.setCostExtractor
            %
            value = ShapeContextDistanceExtractor_(this.id, 'getCostExtractor');
        end

        function value = getTransformAlgorithm(this)
            %GETTRANSFORMALGORITHM  Get the current algorithm used for aligning the shapes
            %
            %     value = obj.getTransformAlgorithm()
            %
            % ## Output
            % * __value__ output scalar struct
            %
            % See also: cv.ShapeContextDistanceExtractor.setTransformAlgorithm
            %
            value = ShapeContextDistanceExtractor_(this.id, 'getTransformAlgorithm');
        end
    end

    %% ShapeDistanceExtractor
    methods
        function dist = computeDistance(this, contour1, contour2)
            %COMPUTEDISTANCE  Compute the shape distance between two shapes defined by its contours
            %
            %     dist = obj.computeDistance(contour1, contour2)
            %
            % ## Options
            % * __contour1__ Contour defining first shape. A numeric
            %   Nx2/Nx1x2/1xNx2 array or a cell-array of 2D points
            %   `{[x,y], ...}`
            % * __contour2__ Contour defining second shape. Same format as
            %   `contours1`.
            %
            % ## Output
            % * __dist__ output distance.
            %
            % See also: cv.ShapeContextDistanceExtractor.ShapeContextDistanceExtractor
            %
            dist = ShapeContextDistanceExtractor_(this.id, 'computeDistance', contour1, contour2);
        end
    end

    %% Algorithm
    methods
        function clear(this)
            %CLEAR  Clears the algorithm state
            %
            %     obj.clear()
            %
            % See also: cv.ShapeContextDistanceExtractor.empty,
            %  cv.ShapeContextDistanceExtractor.load
            %
            ShapeContextDistanceExtractor_(this.id, 'clear');
        end

        function b = empty(this)
            %EMPTY  Returns true if the algorithm is empty
            %
            %     b = obj.empty()
            %
            % ## Output
            % * __b__ Returns true if the detector object is empty (e.g in the
            %   very beginning or after unsuccessful read).
            %
            % See also: cv.ShapeContextDistanceExtractor.clear,
            %  cv.ShapeContextDistanceExtractor.load
            %
            b = ShapeContextDistanceExtractor_(this.id, 'empty');
        end

        function save(this, filename)
            %SAVE  Saves the algorithm parameters to a file
            %
            %     obj.save(filename)
            %
            % ## Input
            % * __filename__ Name of the file to save to.
            %
            % This method stores the algorithm parameters in the specified
            % XML or YAML file.
            %
            % See also: cv.ShapeContextDistanceExtractor.load
            %
            ShapeContextDistanceExtractor_(this.id, 'save', filename);
        end

        function load(this, fname_or_str, varargin)
            %LOAD  Loads algorithm from a file or a string
            %
            %     obj.load(fname)
            %     obj.load(str, 'FromString',true)
            %     obj.load(..., 'OptionName',optionValue, ...)
            %
            % ## Input
            % * __fname__ Name of the file to read.
            % * __str__ String containing the serialized model you want to
            %   load.
            %
            % ## Options
            % * __ObjName__ The optional name of the node to read (if empty,
            %   the first top-level node will be used). default empty
            % * __FromString__ Logical flag to indicate whether the input is a
            %   filename or a string containing the serialized model.
            %   default false
            %
            % This method reads algorithm parameters from the specified XML or
            % YAML file (either from disk or serialized string). The previous
            % algorithm state is discarded.
            %
            % See also: cv.ShapeContextDistanceExtractor.save
            %
            ShapeContextDistanceExtractor_(this.id, 'load', fname_or_str, varargin{:});
        end

        function name = getDefaultName(this)
            %GETDEFAULTNAME  Returns the algorithm string identifier
            %
            %     name = obj.getDefaultName()
            %
            % ## Output
            % * __name__ This string is used as top level XML/YML node tag
            %   when the object is saved to a file or string.
            %
            % See also: cv.ShapeContextDistanceExtractor.save,
            %  cv.ShapeContextDistanceExtractor.load
            %
            name = ShapeContextDistanceExtractor_(this.id, 'getDefaultName');
        end
    end

    %% Getters/Setters
    methods
        function value = get.AngularBins(this)
            value = ShapeContextDistanceExtractor_(this.id, 'get', 'AngularBins');
        end
        function set.AngularBins(this, value)
            ShapeContextDistanceExtractor_(this.id, 'set', 'AngularBins', value);
        end

        function value = get.RadialBins(this)
            value = ShapeContextDistanceExtractor_(this.id, 'get', 'RadialBins');
        end
        function set.RadialBins(this, value)
            ShapeContextDistanceExtractor_(this.id, 'set', 'RadialBins', value);
        end

        function value = get.InnerRadius(this)
            value = ShapeContextDistanceExtractor_(this.id, 'get', 'InnerRadius');
        end
        function set.InnerRadius(this, value)
            ShapeContextDistanceExtractor_(this.id, 'set', 'InnerRadius', value);
        end

        function value = get.OuterRadius(this)
            value = ShapeContextDistanceExtractor_(this.id, 'get', 'OuterRadius');
        end
        function set.OuterRadius(this, value)
            ShapeContextDistanceExtractor_(this.id, 'set', 'OuterRadius', value);
        end

        function value = get.RotationInvariant(this)
            value = ShapeContextDistanceExtractor_(this.id, 'get', 'RotationInvariant');
        end
        function set.RotationInvariant(this, value)
            ShapeContextDistanceExtractor_(this.id, 'set', 'RotationInvariant', value);
        end

        function value = get.ShapeContextWeight(this)
            value = ShapeContextDistanceExtractor_(this.id, 'get', 'ShapeContextWeight');
        end
        function set.ShapeContextWeight(this, value)
            ShapeContextDistanceExtractor_(this.id, 'set', 'ShapeContextWeight', value);
        end

        function value = get.ImageAppearanceWeight(this)
            value = ShapeContextDistanceExtractor_(this.id, 'get', 'ImageAppearanceWeight');
        end
        function set.ImageAppearanceWeight(this, value)
            ShapeContextDistanceExtractor_(this.id, 'set', 'ImageAppearanceWeight', value);
        end

        function value = get.BendingEnergyWeight(this)
            value = ShapeContextDistanceExtractor_(this.id, 'get', 'BendingEnergyWeight');
        end
        function set.BendingEnergyWeight(this, value)
            ShapeContextDistanceExtractor_(this.id, 'set', 'BendingEnergyWeight', value);
        end

        function value = get.Iterations(this)
            value = ShapeContextDistanceExtractor_(this.id, 'get', 'Iterations');
        end
        function set.Iterations(this, value)
            ShapeContextDistanceExtractor_(this.id, 'set', 'Iterations', value);
        end

        function value = get.StdDev(this)
            value = ShapeContextDistanceExtractor_(this.id, 'get', 'StdDev');
        end
        function set.StdDev(this, value)
            ShapeContextDistanceExtractor_(this.id, 'set', 'StdDev', value);
        end
    end

end


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

%*****************************************************************************80
%
%% NORMAL_01_CDF_VALUES returns some values of the Normal 01 CDF.
%
%  Discussion:
%
%    In Mathematica, the function can be evaluated by:
%
%      Needs["Statistics`ContinuousDistributions`"]
%      dist = NormalDistribution [ 0, 1 ]
%      CDF [ dist, x ]
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    30 September 2004
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Milton Abramowitz and Irene Stegun,
%    Handbook of Mathematical Functions,
%    US Department of Commerce, 1964.
%
%    Stephen Wolfram,
%    The Mathematica Book,
%    Fourth Edition,
%    Wolfram Media / Cambridge University Press, 1999.
%
%  Parameters:
%
%    Input/output, integer N_DATA.  The user sets N_DATA to 0 before the
%    first call.  On each call, the routine increments N_DATA by 1, and
%    returns the corresponding data; when there is no more data, the
%    output value of N_DATA will be 0 again.
%
%    Output, real X, the argument of the function.
%
%    Output, real FX, the value of the function.
%
  n_max = 17;

  fx_vec = [ ...
     0.5000000000000000E+00, ...
     0.5398278372770290E+00, ...
     0.5792597094391030E+00, ...
     0.6179114221889526E+00, ...
     0.6554217416103242E+00, ...
     0.6914624612740131E+00, ...
     0.7257468822499270E+00, ...
     0.7580363477769270E+00, ...
     0.7881446014166033E+00, ...
     0.8159398746532405E+00, ...
     0.8413447460685429E+00, ...
     0.9331927987311419E+00, ...
     0.9772498680518208E+00, ...
     0.9937903346742239E+00, ...
     0.9986501019683699E+00, ...
     0.9997673709209645E+00, ...
     0.9999683287581669E+00 ];

  x_vec = [ ...
     0.0000000000000000E+00, ...
     0.1000000000000000E+00, ...
     0.2000000000000000E+00, ...
     0.3000000000000000E+00, ...
     0.4000000000000000E+00, ...
     0.5000000000000000E+00, ...
     0.6000000000000000E+00, ...
     0.7000000000000000E+00, ...
     0.8000000000000000E+00, ...
     0.9000000000000000E+00, ...
     0.1000000000000000E+01, ...
     0.1500000000000000E+01, ...
     0.2000000000000000E+01, ...
     0.2500000000000000E+01, ...
     0.3000000000000000E+01, ...
     0.3500000000000000E+01, ...
     0.4000000000000000E+01 ];

  if ( n_data < 0 )
    n_data = 0;
  end

  n_data = n_data + 1;

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

  return
end


%CONFMAT Construct confusion matrix
%
%  [C,NE,LABLIST] = CONFMAT(LAB1,LAB2,METHOD,FID)
%
% INPUT
%  LAB1        Set of labels
%  LAB2        Set of labels
%  METHOD      'count' (default) to count number of co-occurences in
%	             LAB1 and LAB2, 'disagreement' to count relative
%		           non-co-occurrence.
%  FID         Write text result to file
%
% OUTPUT
%  C           Confusion matrix
%  NE          Total number of errors (empty labels are neglected)
%  LABLIST     Unique labels in LAB1 and LAB2
%
% DESCRIPTION
% Constructs a confusion matrix C between two sets of labels LAB1
% (corresponding to the rows in C) and LAB2 (the columns in C). The order of
% the rows and columns is returned in LABLIST. NE is the total number of
% errors (sum of non-diagonal elements in C).
%
% When METHOD = 'count' (default), co-occurences in LAB1 and LAB2 are counted
% and returned in C. When METHOD = 'disagreement', the relative disagreement
% is returned in NE, and is split over all combinations of labels in C
% (such that the rows sum to 1). (The total disagreement for a class equals
% one minus the sensitivity for that class as computed by TESTC).
%
%   [C,NE,LABLIST] = CONFMAT(D,METHOD)
%
% If D is a classification result D = A*W, the labels LAB1 and LAB2 are
% internally retrieved by CONFMAT before computing the confusion matrix.
%
% When no output argument is specified, or when FID is given, the
% confusion matrix is displayed or written a to a text file. It is assumed
% that LAB1 contains true labels and LAB2 stores estimated labels.
%
% EXAMPLE
% Typical use of CONFMAT is the comparison of true and and estimated labels
% of a testset A by application to a trained classifier W:
% LAB1 = GETLABELS(A); LAB2 = A*W*LABELD.
% More examples can be found in PREX_CONFMAT, PREX_MATCHLAB.
%
% SEE ALSO
% MAPPINGS, DATASETS, GETLABELS, LABELD

% Copyright: R.P.W. Duin, r.p.w.duin@prtools.org
% Faculty EWI, Delft University of Technology
% P.O. Box 5031, 2600 GA Delft, The Netherlands

% $Id: confmat.m,v 1.7 2008/10/14 21:34:32 duin Exp $

function [CC,ne,lablist,lablist_true] = confmat_new (arg1,arg2,arg3,fid,lablist_true)

	prtrace(mfilename);

	% Check arguments.
  if nargin < 5, lablist_true = []; end
  if nargin < 4, fid = 1; end
	if nargin < 3 | isempty(arg3)
		if isdataset(arg1)
			lab1 = getlabels(arg1); lab2 = arg1*labeld;
			if nargin < 2| isempty(arg2)
				method = 'count';
				prwarning(4,'no method supplied, assuming count');
			else
				method = arg2;
			end
		else
			method = 'count';
			prwarning(4,'no method supplied, assuming count');
			lab1 = arg1;
			if (nargin < 2 | isempty(arg2))
				error('prtools_addin','Second label list not supplied')
			end
			lab2 = arg2;
		end
	else
		lab1 = arg1;
		lab2 = arg2;

        method = arg3;
	end
	if nargin < 2
		if ~isdataset(arg1)
			error('prtools_addin','two labellists or one dataset should be supplied')
		end
	end

	% Renumber LAB1 and LAB2 and find number of unique labels.

	m = size(lab1,1);
	if (m~=size(lab2,1))
		error('prtools_addin','LAB1 and LAB2 have to have the same lengths.');
    end

    if( isempty(lablist_true) )

        [nlab1,nlab2,lablist] = renumlab(lab1,lab2);
    % 	n = max(nlab1);
    % 	n = max(nlab2);
    % 	n = max(n,n);
        n = size(lablist,1);

    else
        nlab1 = renumlab(lab1,lablist_true);
        nlab2 = renumlab(lab2,lablist_true);
        lablist = lablist_true;
        n = size(lablist,1);
    end

	% Construct matrix of co-occurences (confusion matrix).

	C = zeros(n+1,n+1);
	for i = 0:n
		K = find(nlab1==i);
		if (isempty(K))
			C(i+1,:) = zeros(1,n+1);
		else
			for j = 0:n
				C(i+1,j+1) = length(find(nlab2(K)==j));
			end
		end
	end

	% position rejects and unlabeled object at the end of the matrix

	D = C;
	D(1:end-1,1:end-1) = C(2:end,2:end);
	D(end,:) = [C(1,2:end) C(1,1)];
	D(1:end-1,end) = C(2:end,1);
	C = D;
	D = D(1:end-1,1:end-1);
	DD = D(1:min(n,n),1:min(n,n));
	% Calculate number of errors ('count') or disagreement ('disagreement').
	% Neglect rejects

  if nargout > 1
    J = find(nlab1~=0 & nlab2~=0);
    ne = nlabcmp(lab1(J,:),lab2(J,:));
  end

	switch (method)
		case 'count'
			ne = sum(sum(D)) - sum(diag(DD));       % Diagonal entries are correctly
                                                % classified, so all off-diagonal
                                                % entries denote wrong ones.

		case 'disagreement'
			ne = (sum(sum(D)) - sum(diag(DD)))/m;   % Relative sum of off-diagonal
                                             % entries.
			E = repmat(sum(D,2),1,n);              % Disagreement = 1 -
			D = ones(n,n)-D./E;                    % relative co-occurence.
			D = D / (n-1);
		otherwise
			error('prtools_addin','unknown method');
	end

	%Distinguish 'rejects / no_labels' from 'non_rejects / fully labeled'
	if (any(C(:,end) ~= 0) | any(C(end,:)~=0)) & strcmp(method,'count')
		n = n+1;
        n_real = n;
		labch = char(strlab(lablist),'reject');
		labcv = char(strlab(lablist),'No');
    else
        if( isempty(lablist_true) )
            labcv = strlab(lablist);
            n_real = n;
        else
            labcv = lablist_true;
            n_real = size(lablist_true,1);
        end
        labch = strlab(lablist);
		%labcv = labch;
		C = D;
	end

    % If no output argument is specified, pretty-print C.

	if (nargout == 0) | nargin == 4

		if nargin < 4, fid = 1; end

		% Make sure labels are stored in LABC as matrix of characters,
    % max. 6 per label.

		if (size(labch,2) > 6)
			labch = labch(:,1:6);
			%labcv = labcv(:,1:6);
		end
		if (size(labch,2) < 5)
			labch = [labch repmat(' ',n,ceil((5-size(labch,2))/2))];
% 			labcv = [labcv repmat(' ',n,ceil((5-size(labcv,2))/2))];
		end

		%C = round(1000*C./repmat(sum(C,2),1,size(C,2)));

		nspace = max(size(labcv,2)-7,0);
		cspace = repmat(' ',1,nspace);
		%fprintf(fid,['\n' cspace '        | Estimated Labels']);
		fprintf(fid,['\n  True   ' cspace '| Estimated Labels']);
		fprintf(fid,['\n  Labels ' cspace '|']);
		for j = 1:n, fprintf(fid,'%7s',labch(j,:)); end
		fprintf(fid,'|');
		fprintf(fid,' Totals');
		fprintf(fid,'\n ');
		fprintf(fid,repmat('-',1,8+nspace));
		fprintf(fid,'|%s',repmat('-',1,7*n));
		fprintf(fid,'|-------');
		fprintf(fid,'\n ');

		for j = 1:min(n,n_real)
			fprintf(fid,' %-7s|',labcv(j,:));
			switch (method)
				case 'count'
					fprintf(fid,'%5i  ',C(j,:)');
					fprintf(fid,'|');
					fprintf(fid,'%5i',sum(C(j,:)));
				case 'disagreement'
					fprintf(fid,' %5.3f ',C(j,:)');
					fprintf(fid,'|');
					fprintf(fid,' %5.3f ',sum(C(j,:)));
			end
			fprintf(fid,'\n ');
		end

		fprintf(fid,repmat('-',1,8+nspace));
		fprintf(fid,'|%s',repmat('-',1,7*n));
		fprintf(fid,'|-------');
		fprintf(fid,['\n  Totals ' cspace '|']);

		switch (method)
			case 'count'
				fprintf(fid,'%5i  ',sum(C));
				fprintf(fid,'|');
				fprintf(fid,'%5i',sum(C(:)));
			case 'disagreement'
				fprintf(fid,' %5.3f ',sum(C));
				fprintf(fid,'|');
				fprintf(fid,' %5.3f ',sum(C(:)));
		end
		fprintf(fid,'\n\n');
	end

	if nargout > 0
		CC = C;
	end

return


function [f] = spm_mc_fxa_4(x,v,a,P)
% equations of motion for the mountain car problem
% problem
% FORMAT [f] = spm_mc_fxa_4(x,v,a,P)
%
% x   - hidden states
% v   - exogenous inputs
% a   - action
% P   - parameters for mountain car
%
% returns f = dx/dt
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Karl Friston
% $Id: spm_mc_fxa_4.m 3333 2009-08-25 16:12:44Z karl $

% physical flow
%--------------------------------------------------------------------------
dx  = spm_fx_mountaincar([x.x; x.v],v,a,P)/2;
f.x = dx(1);
f.v = dx(2);

% physiological flow
%--------------------------------------------------------------------------
A   = exp(-(x.x - 1).^2*32);
f.p = A - x.p/32;
f.d = (x.p - x.d)/64;