% DIPOLEDENSITY - compute and optionally plot a measure of the 3-D spatial
%                   (in)homogeneity of a specified (large) set of 1- or 2-dipole
%                   component models, either as physical dipole density or as
%                   dipole-position entropy across subjects. In either case,
%                   take into account either all the dipoles, or only the nearest
%                   dipole from each of the subjects. If no output arguments,
%                   or if 'plot','on', paints a 3-D density|entropy brain image
%                   on slices of the Montreal Neurological Institute (MNI) mean
%                   MR brain image ('standard_BESA/avg152t1.mat'). Calls
%                   DIPPLOT,
%                   MRI3DPLOT, and Fieldtrip function FT_INSIDE_HEADMODEL.
% Usage:
%               >> [dens3d mri] = dipoledensity( dipoles, 'key',val, ... );
%
% Inputs:
%    dipoles - this may be either the same dipole structure given as input to
%              the DIPPLOT function, a 3 by n array of dipole localization or
%              a cell array containing arguments for the dipplot function. Note that
%              the 'coordformat' option below defines the coordinate space for these
%              dipoles (default is MNI). See help dipplot for more information.
%
% Optional 'key', val input pairs:
%  'mri'        - [string or struct] mri file (matlab format or file format read
%                 by fcdc_read_mri). See dipplot.m help for more information.
%  'method'     - ['alldistance'|'distance'|'entropy'|'relentropy'] method for
%                            computing density:
%                 'alldistance' - {default} take into account the gaussian-weighted
%                            distances from each voxel to all the dipoles. See
%                            'methodparam' (below) to specify a standard deviation
%                            (in mm) for the gaussian weight kernel.
%                 'distance' - take into account only the distances to the nearest
%                              dipole for each subject. See 'methodparam' (below).
%                 'entropy' - taking into account only the nearest dipole to each
%                             voxel for each subject. See 'methodparam' below.
%                 'relentropy' - as in 'entropy,' but take into account all the
%                             dipoles for each subject.
% 'methodparam' - [number] for 'distance'|'alldistance' methods (see above), the
%                 standard deviation (in mm) of the 3-D gaussian smoothing kernel.
%                 For 'entropy'|'relentropy' methods, the number of closest dipoles
%                 to include {defaults: 20 mm | 20 dipoles }
% 'subsample'   - [integer] subsampling of native MNI image {default: 2 -> 2x2x2}
% 'weight'      - [(1,ncomps) array] for 'distance'|'alldistance' methods, the
%                 relative weight of each component dipole {default: ONES}
% 'coordformat' - ['mni'|'spherical'] coordinate format if dipole location or
%                 a structure is given as input. Default is 'mni'.
% 'subjind'     - [(1,ncomps) array] subject index for each dipole model. If two
%                 dipoles are in one component model, give only one subject index.
% 'nsessions'   - [integer] for 'alldistance' method, the number of sessions to
%                 divide the output values by, so that the returned measure is
%                 dipole density per session {default: 1}
% 'plot'        - ['on'|'off'] force plotting dipole density|entropy
%                 {default: 'on' if no output arguments, else 'off'}
% 'dipplot'     - ['on'|'off'] plot the dipplot image (used for converting
%                 coordinates (default is 'off')
% 'plotargs'    - {cell array} plotting arguments for MRI3DPLOT function.
% 'volmesh_fname' - [string] precomputed mesh volume file name. If not
%                 given as input the function will recompute it (it can take from
%                 five to 20 minutes). By default this function save the volume file
%                 mesh into a file named volmesh_local.mat in the current
%                 folder.
% 'norm2JointProb' - ['on'|'off'] Use joint probability (i.e. sum of all
%                    voxel values == 1) instead of number of dipoles/cm^3.
%                    Should be used for group comparison. (default 'off')
%
% Outputs:
%  dens3d       - [3-D num array] density in dipoles per cubic centimeter. If output
%                 is returned, no plot is produced unless 'plot','on' is specified.
%  mri          - {MRI structure} used in MRI3DPLOT.
%
% Example:
%         >> fakedipoles = (rand(3,10)-0.5)*80;
%         >> [dens3d mri] = dipoledensity( fakedipoles, 'coordformat', 'mni');
%         >> mri3dplot(dens3d,mri); % replot if no output is given above
%                                   % function is called automatically
%
% ------------------------------------
% NOTES:   to do multiple subject causal-weighted density map,
% (1) concatenate dipplot coord matrices for all subject
% (2) make g.subjind vector [ones(1,ncompsS1) 2*ones(1,ncompsS2) ... N*ones(1,ncompssN)]
% (3) concatenate normalized outflows for all subjects to form weight vector
% (4) call dipoledensity function with method = 'entropy' or 'relentropy'
% ------------------------------------
%
% See also:
%           EEGLAB: DIPPLOT, MRI3DPLOT, Fieldtrip: FT_INSIDE_HEADMODEL
%
% Authors: Arnaud Delorme & Scott Makeig SCCN, INC, UCSD
% Modified by: Makoto Miyakoshi
%              Ramon Martinez-Cancino
% Copyright (C) Arnaud Delorme & Scott Makeig, SCCN/INC/UCSD, 2003-
%
% 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.

function [prob3d, mri] = dipoledensity(dipplotargs, varargin)

    % TO DO: return in dipplot() the real 3-D location of dipoles (in posxyz)
    %        FIX the dimension order here

prob3d = []; mri = [];
if nargin < 1
   help dipoledensity
   return
end

g = finputcheck(varargin, { 'subjind'     'integer'  []               [];
                            'method' 'string' { 'relentropy','entropy','distance','alldistance' } 'alldistance';
                            'methodparam' 'real'     []               20;
                            'weight'      { 'real','cell' }  []               [];
                            'smooth'      'real'     []               0;
                            'nsessions'   'integer'  []               1;
                            'subsample'   'integer'  []               2;
                            'plotargs'    'cell'     []               {};
                            'plot'        'string'  { 'on','off' }    fastif(nargout == 0, 'on', 'off');
                            'dipplot'     'string'  { 'on','off' }   'off';
                            'coordformat' 'string'  { 'mni','spherical' }   'mni';
                            'normalization' 'string'  { 'on','off' } 'on';
                            'volmesh_fname' 'string'  []  'volmesh_local.mat';
                            'mri'         { 'struct','string' } [] '';
                            'norm2JointProb' 'string'  { 'on','off' } 'off'});
if ischar(g), error(g); end
if ~strcmpi(g.method, 'alldistance') && isempty(g.subjind)
    error('Subject indices are required for this method');
end
if ~iscell(g.weight), g.weight = { g.weight }; end

% plotting dipplot
% ----------------
if ~iscell(dipplotargs) % convert input
    if ~isstruct(dipplotargs)
        if size(dipplotargs,1) == 3, dipplotargs = dipplotargs';
        elseif size(dipplotargs,2) ~= 3
            error('If an array of dipoles is given as entry, there must be 3 columns or 3 rows for x y z');
        end
        model = [];
        for idip = 1:length(dipplotargs)
            model(idip).posxyz = dipplotargs(idip,:);
            model(idip).momxyz = [1 0 0];
            model(idip).rv = 0.5;
        end
        dipplotargs = model;
    end
    dipplotargs = { dipplotargs 'coordformat' g.coordformat };
else
    dipplotargs = { dipplotargs{:} 'coordformat' g.coordformat };
end
struct = dipplot(dipplotargs{:}, 'plot', g.dipplot, 'density', 'off');
if nargout == 0
    drawnow;
end

% retrieve coordinates in MNI space
% ---------------------------------
if 0 % deprecated
     % find dipoles
     % ------------
    hmesh = findobj(gcf, 'tag', 'mesh');
    if isempty(hmesh), error('Current figure must contain dipoles'); end
    hh = [];
    disp('Finding dipoles...');
    dips = zeros(1,200);
    for index = 1:1000
        hh = [ hh(:); findobj(gcf, 'tag', ['dipole' int2str(index) ]) ];
        dips(index) = length(findobj(gcf, 'tag', ['dipole' int2str(index) ]));
    end

    disp('Retrieving dipole positions ...');
    count = 1;
    for index = 1:length(hh)
        tmp = get(hh(index), 'userdata');
        if length(tmp) == 1
            allx(count) = tmp.eleccoord(1,1);
            ally(count) = tmp.eleccoord(1,2);
            allz(count) = tmp.eleccoord(1,3);
            alli(count) = index;
            count = count + 1;
        end
    end
end

% check weights
% -------------
if ~isempty(g.weight{1})
    if ~iscell(g.weight)
        if length(g.weight) ~= length(struct)
            error('There must be as many elements in the weight matrix as there are dipoles')
        end
    else
        if length(g.weight{1}) ~= length(struct) || length(g.weight{1}) ~= length(g.weight{end})
            error('There must be as many elements in the weight matrix as there are dipoles')
        end
    end
else
    g.weight = { ones( 1, length(struct)) };
end
if ~isempty(g.subjind)
    if length(g.subjind) ~= length(struct)
        error('There must be as many element in the subject matrix as there are dipoles')
    end
else
    g.subjind = ones( 1, length(struct));
end

% decoding dipole locations
% -------------------------
disp('Retrieving dipole positions ...');
count = 1;
for index = 1:length(struct)
    dips = size(struct(index).eleccoord,1);
    for dip = 1:dips
        allx(count) = struct(index).eleccoord(dip,1);
        ally(count) = struct(index).eleccoord(dip,2);
        allz(count) = struct(index).eleccoord(dip,3);
        alli(count) = index;
        allw1(count) = g.weight{1}(  index)/dips;
        allw2(count) = g.weight{end}(index)/dips;
        alls(count) = g.subjind(index);
        count = count + 1;
    end
end
g.weight{1}    = allw1;
g.weight{end}  = allw2;
g.subjind = alls;

% read MRI file
% -------------
if isempty(g.mri) % default MRI file
    dipfitdefs;
    load('-mat', template_models(1).mrifile); % load mri variable
    g.mri = mri;
end
if ischar(g.mri)
    try
        mri = load('-mat', g.mri);
        mri = mri.mri;
    catch
        disp('Failed to read Matlab file. Attempt to read MRI file using function read_fcdc_mri');
        try
            warning off;
            mri = read_fcdc_mri(g.mri);
            mri.anatomy = round(gammacorrection( mri.anatomy, 0.8));
            mri.anatomy = uint8(round(mri.anatomy/max(reshape(mri.anatomy, prod(mri.dim),1))*255));
            % WARNING: if using double instead of int8, the scaling is different
            % [-128 to 128 and 0 is not good]
            % WARNING: the transform matrix is not 1, 1, 1 on the diagonal, some slices may be
            % misplaced
            warning on;
        catch
            error('Cannot load file using read_fcdc_mri');
        end
    end
    g.mri = mri; % output the anatomic mri image
end

% reserve array for density
% -------------------------
prob3d = {zeros(ceil(g.mri.dim/g.subsample)) };
for i = 2:length(g.weight), prob3d{i} = prob3d{1}; end

% compute voxel size
% ------------------
point1 = g.mri.transform * [ 1 1 1 1 ]';
point2 = g.mri.transform * [ 2 2 2 1 ]';
voxvol = sum((point1(1:3)-point2(1:3)).^2)*g.subsample^3; % in mm

% compute global subject entropy if necessary
% -------------------------------------------
vals   = unique_bc(g.subjind); % the unique subject indices
if strcmpi(g.method, 'relentropy') || strcmpi(g.method, 'entropy') %%%%% entropy %%%%%%%
    newind = zeros(size(g.subjind));
    for index = 1:length(vals) % foreach subject in the cluster
        tmpind = find(g.subjind == vals(index)); % dipoles for the subject
        totcount(index) = length(tmpind); % store the number of subject dipoles
        newind(tmpind) = index; % put subject index into newind
    end
    g.subjind = newind;
    gp = totcount/sum(totcount);
    globent = -sum(gp.*log(gp));
end

% compute volume inside head mesh
% -------------------------------
dipfitdefs; % get the location of standard BEM volume file
tmp = load('-mat',DIPOLEDENSITY_STDBEM); % load MNI mesh

if ~exist(g.volmesh_fname)
    % Checking for Fieldtrip
    if exist('ft_electroderealign', 'file')~=2,error('dipoledensity: Fieldtrip toolbox is required'); end

    disp('Computing volume within head mesh...');
    [X Y Z]           = meshgrid(g.mri.xgrid(1:g.subsample:end)+g.subsample/2, ...
                                 g.mri.ygrid(1:g.subsample:end)+g.subsample/2, ...
                                 g.mri.zgrid(1:g.subsample:end)+g.subsample/2);
    [indX indY indZ ] = meshgrid(1:length(g.mri.xgrid(1:g.subsample:end)), ...
                                 1:length(g.mri.ygrid(1:g.subsample:end)), ...
                                 1:length(g.mri.zgrid(1:g.subsample:end)));
    allpoints = [ X(:)'    ; Y(:)'   ; Z(:)' ];
    allinds   = [ indX(:)' ; indY(:)'; indZ(:)' ];
    allpoints = g.mri.transform * [ allpoints ; ones(1, size(allpoints,2)) ];
    allpoints(4,:) = [];

    olddir = pwd;
    tmppath = which('ft_electroderealign');
    tmppath = fullfile(fileparts(tmppath), 'private');
    cd(tmppath);
    inside = ft_inside_headmodel(allpoints', tmp.vol);
    Inside = find(inside); Outside = find(~inside);
    cd(olddir);
    disp('Done.');

    if 0 % old code using Delaunay %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        P = tmp.vol.bnd(1).pnt;
        T = delaunayn(P); % recompute triangularization (the original one is not compatible
                          % with tsearchn) get coordinates of all points in the volume
        % search for points inside or outside the volume (takes about 14 minutes!)
        IO = tsearchn(P, T, allpoints');
        Inside        = find(isnan(IO));
        Outside       = find(~isnan(IO));
        disp('Done.');
    end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    try
        save('-mat', g.volmesh_fname, 'allpoints', 'allinds', 'Inside', 'Outside');
        disp('Saving file containing inside/outide voxel indices...');
    catch, end
else
    disp('Loading file containing inside/outide voxel indices...');
    load('-mat',g.volmesh_fname);
end
InsidePoints  = allpoints(:, Inside);
InsideIndices = allinds(:, Inside);

% scan grid and compute entropy at each voxel
% -------------------------------------------
edges = [0.5:1:length(vals)+0.5];

if ~strcmpi(g.method, 'alldistance')
    fprintf('Computing (of %d):', size(InsideIndices,2));
    % entropy calculation: have to scan voxels
    % ----------------------------------------
    for i = 1:size(InsideIndices,2)

        alldists = (InsidePoints(1,i) - allx).^2 ...
                 + (InsidePoints(2,i) - ally).^2 ...
                 + (InsidePoints(3,i) - allz).^2;
        [tmpsort indsort] = sort(alldists); % sort dipoles by distance
        tmpweights{1}   = g.weight{1}(  indsort);
        tmpweights{end} = g.weight{end}(indsort);

        if strcmpi(g.method, 'relentropy') || strcmpi(g.method, 'entropy') %%%%% entropy %%%%%%%

            subjs  = g.subjind(indsort(1:g.methodparam)); % get subject indices of closest dipoles
            p      = histc(subjs, edges);
            if strcmpi(g.method, 'relentropy')
                p      = p(1:end-1)./totcount;
                % this should be uniform if p conforms to global count for all subjects
            end
            p      = p/sum(p);
            p(find(p == 0)) = [];
            for tmpi = 1:length(g.weight)
                prob3d{1}(InsideIndices(1,i), InsideIndices(2,i), InsideIndices(3,i)) = -sum(p.*log(p));
            end
        else
            % distance to each subject
            ordsubjs  = g.subjind(indsort);
            for index = 1:length(vals) % for each subject
                tmpind = find(ordsubjs == vals(index));
                if strcmpi(g.method,'distance')
                    use_dipoles(index) = tmpind(1); % find their nearest dipole
                end
            end
            for tmpi = 1:length(g.weight)
                prob3d{tmpi}(InsideIndices(1,i), InsideIndices(2,i), InsideIndices(3,i)) = ...
                   sum(tmpweights{tmpi}(use_dipoles).*exp(-tmpsort(use_dipoles)/ ...
                           (2*g.methodparam^2))); % 3-D gaussian smooth
            end
        end
        if mod(i,100) == 0, fprintf('%d ', i); end
    end
else % 'alldistance'
    % distance calculation: can scan dipoles instead of voxels (since linear)
    % --------------------------------------------------------
    %alldists = allx.^2 + ally.^2 + allz.^2;
    %figure; hist(alldists); return; % look at distribution of distances

    fprintf('Computing (of %d):', size(allx,2));
    for tmpi=1:length(g.weight)
        tmpprob{tmpi} = zeros(1, size(InsidePoints,2));
    end
    if length(g.weight) > 1, tmpprob2 = tmpprob; end
    for i = 1:size(allx,2)
        alldists = (InsidePoints(1,:) - allx(i)).^2 + ...
                   (InsidePoints(2,:) - ally(i)).^2 + ...
                   (InsidePoints(3,:) - allz(i)).^2;
%         alldists = 1;  % TM
        for tmpi=1:length(g.weight)
            tmpprob{tmpi} = tmpprob{tmpi} + g.weight{tmpi}(i)*exp(-alldists/(2*g.methodparam^2)); % 3-D gaussian smooth
            if any(isinf(tmpprob{tmpi})), error('Infinite value in probability calculation'); end
        end
        if mod(i,50) == 0, fprintf('%d ', i); end
    end
    % copy values to 3-D mesh
    % -----------------------
    for i = 1:length(Inside)
        pnts = allinds(:,Inside(i));
        for tmpi = 1:length(g.weight)
            prob3d{tmpi}(pnts(1), pnts(2), pnts(3)) = tmpprob{tmpi}(i);
        end
    end

end
fprintf('\n');

% normalize for points inside and outside the volume
% norm2JointProb is applied before plotting
% --------------------------------------------------
if strcmpi(g.method, 'alldistance') && strcmpi(g.normalization,'on')
    for i =1:length(g.weight)
        disp('Normalizing to dipole/mm^3');
        if any(prob3d{i}(:)<0)
            fprintf('WARNING: Some probabilities are negative, this will likely cause problems when normalizing probabilities.\n');
            fprintf('It is highly recommended to turn normaliziation off by using ''normalization'' key to ''off''.\n');
        end
        totval = sum(prob3d{i}(:));                                     % total values in the head
        totdip = size(allx,2);                                          % number of dipoles
        prob3d{i} = (prob3d{i}/totval*totdip/voxvol*1000)/g.nsessions;  % time 1000 to get cubic centimeters
    end
end

% resample matrix
% ----------------
if g.subsample ~= 1
    for i =1:length(g.weight)
        prob3d{i} = prob3d{i}/g.subsample;
        newprob3d = zeros(g.mri.dim);
        X = ceil(g.mri.xgrid/g.subsample);
        Y = ceil(g.mri.ygrid/g.subsample);
        Z = ceil(g.mri.zgrid/g.subsample);
        for index = 1:size(newprob3d,3)
            newprob3d(:,:,index) = prob3d{i}(X,Y,Z(index));
        end
        prob3d{i} = newprob3d;
    end
end

% 3-D smoothing
% -------------
if g.smooth ~= 0
    disp('Smoothing...');
    for i =1:length(g.weight)
        prob3d{i} = smooth3d(prob3d{i}, g.smooth);
    end
end

% Perform normalization so that the total sum of joint prob == 1
if strcmpi(g.norm2JointProb, 'on')
    prob3d{i} = prob3d{i}/sum(prob3d{i}(:));
end

% plotting
% --------
if strcmpi(g.plot, 'off')
    close gcf;
else
    mri3dplot( prob3d, g.mri, g.plotargs{:}); % plot the density using mri3dplot()
end
return;


def PlotFrame(aedat, numPlots, distributeBy, minTime, maxTime, transpose, flipVertical, flipHorizontal)

%{
Takes 'aedat' - a data structure containing an imported .aedat file,
as created by ImportAedat, and creates a series of images from selected
frames.
The number of subplots is given by the numPlots parameter.
'distributeBy' can either be 'time' or 'events', to decide how the points
around which data is rendered are chosen.
The frame events are then chosen as those nearest to the time points.
If the 'distributeBy' is 'time' then if the further parameters 'minTime'
and 'maxTime' are used then the time window used is only between
those limits.
flipVertical is assumed true, so that y=0 is considered the top of the image.
%}

if ~exist('distributeBy', 'var')
	distributeBy = 'time';
end

if ~exist('numPlots', 'var')
	numPlots = 12;
end

% This function assumes that aedat3 frame timestamps have been simplified
timeStamps = aedat.data.frame.timeStampStart;
% This function plots all frames, assuming that they are valid;
% it assumes that reset read subtraction has been performed
numFrames = length(aedat.data.frame.samples);
if numFrames < numPlots
	numPlots = numFrames;
end

if numFrames == numPlots
	distributeBy = 'events';
end

% Distribute plots in a raster with a 3:4 ratio
numPlotsX = round(sqrt(numPlots / 3 * 4));
numPlotsY = ceil(numPlots / numPlotsX);

if strcmpi(distributeBy, 'time')
    if ~exist('minTime', 'var') || (exist('minTime', 'var') && minTime == 0)
        minTime = min(timeStamps);
    else
        minTime = minTime * 1e6;
    end
    if ~exist('maxTime', 'var') || (exist('maxTime', 'var') && maxTime == 0)
        maxTime = max(timeStamps);
    else
        maxTime = maxTime * 1e6;
    end

	totalTime = maxTime - minTime;
	timeStep = totalTime / numPlots;
	timePoints = minTime + timeStep * 0.5 : timeStep : maxTime;
else % distribute by event number
	framesPerStep = numFrames / numPlots;
	timePoints = timeStamps(ceil(framesPerStep * 0.5 : framesPerStep : numFrames));
end

if numPlots > 1
    figure
end
for plotCount = 1 : numPlots
	if numPlots > 1
        subplot(numPlotsY, numPlotsX, plotCount);
	end
	hold all
	% Find eventIndex nearest to timePoint
	frameIndex = find(timeStamps >= timePoints(plotCount), 1, 'first');
	% Ignore colour for now ...
	if exist('transpose', 'var') && transpose
        imagesc(aedat.data.frame.samples{frameIndex}')
    else
        imagesc(aedat.data.frame.samples{frameIndex})
	end
    colormap('gray')
	axis equal tight
	if ~exist('flipVertical', 'var') || ~flipVertical
		set(gca, 'YDir', 'reverse')
	end
	if exist('flipHorizontal', 'var') && flipHorizontal
		set(gca, 'XDir', 'reverse')
	end
	title(['Time: ' num2str(round(double(timeStamps(frameIndex)) / 1000) /1000) ' s; frame number: ' num2str(frameIndex)])
end


function [output] = volumethreshold(input, threshold, tissuelabel)

% VOLUMETHRESHOLD is a helper function for segmentations. It applies a
% relative threshold and subsequently looks for the largest connected part,
% thereby removing small blobs such as vitamine E capsules.
%
% See also VOLUMEFILLHOLES, VOLUMESMOOTH, VOLUMEPAD

if nargin<2 || isempty(threshold)
  threshold = 0;
end
if nargin<3 || isempty(tissuelabel)
  tissuelabel = 'volume';
end

% ensure that SPM is available, needed for spm_bwlabel
hasspm = ft_hastoolbox('spm8up', 3) || ft_hastoolbox('spm2', 1);

% mask by taking the negative of the segmentation, thus ensuring
% that no holes are within the compartment and do a two-pass
% approach to eliminate potential vitamin E capsules etc.

% ensure the input volume to be double precision, to allow for
% computations, unless it's already boolean
if ~isa(input, 'double') && ~isa(input, 'logical')
  input = double(input);
end

if ~isa(input, 'logical')
  if nargin==2, ft_error('if the input volume is not a boolean volume, you need to define a threshold value'); end
  if nargin==3, fprintf('thresholding %s at a relative threshold of %0.3f\n', tissuelabel, threshold); end
  output = double(input>(threshold*max(input(:))));
else
  % there is no reason to apply a threshold, but spm_bwlabel still needs a
  % double input for clustering
  output = double(input);
end

% cluster the connected tissue
[cluster, n] = spm_bwlabel(output, 6);

if n>1
  % it pays off to sort the cluster assignment if there are many clusters
  tmp = cluster(:);                       % convert to a vector
  tmp = tmp(tmp>0);                       % remove the zeros
  tmp = sort(tmp, 'ascend');              % sort according to cluster number
  m   = zeros(1,n);
  for k=1:n
    m(k) = sum(tmp==k);       % determine the voxel count for each cluster
    tmp  = tmp(m(k)+1:end);   % remove the last cluster that was counted
  end
  % select the tissue that has the most voxels belonging to it
  [m, i] = max(m);
  output = (cluster==i);
else
  % the output only contains a single cluster
  output = (cluster==1);
end


function out=lagx(X,p)

% function out=lagX(X,p)
% returns lagged values of a matrix, sequentially lagging by 0,1,...,p periods and returning all the lags stacked in a single matrix
% inputs:  - matrix 'X': data matrix
%          - integer 'p': number of lags
% outputs: -matrix 'out': matrix of current and lagged series

% Compute the number of rows and columns of input matrix X, and save the values as r and c
[r,c]=size(X);
out = zeros(numel(p+1:r), size(X,2));
% start with the reformating of the original vectors
out(:,1:c)=X(p+1:r,:);

% treat each lag consecutively
for ii=1:p
out(:,c*ii+1:c*(ii+1))=X(p+1-ii:r-ii,:);
end


% jMap:  Mapping scattered data using local polynomial fitting
%
%   polysmooth - Smoothing scattered 2D data with local polynomial fitting.
%   spheresort - Sorted great circle distances to nearby points on the earth.
%   twodsort   - Distances from data points to nearby grid points.
%
%   Low level functions
%   matinv     - Fast inversion of arrays of small matrices.

help jMap


function out= proc_average(epo, varargin)
%PROC_AVERAGE - Classwise calculated averages
%
%Synopsis:
% EPO= proc_average(EPO, <OPT>)
% EPO= proc_average(EPO, CLASSES)
%
%Arguments:
% EPO -      data structure of epoched data
%            (can handle more than 3-dimensional data, the average is
%            calculated across the last dimension)
% OPT struct or property/value list of optional arguments:
%  .Policy - 'mean' (default), 'nanmean', or 'median'
%  .Std    - if true, standard deviation is calculated also
%  .Stats  - if true, additional statistics are calculated, including the
%            standard error of the mean, the t score, the p-value for the null
%            Hypothesis that the mean is zero, and the "signed log p-value"
%  .Classes - classes of which the average is to be calculated,
%            names of classes (strings in a cell array), or 'ALL' (default)
% 'Bonferroni' - if true, Bonferroni corrected is used to adjust p-values
%                and their logarithms
% 'Alphalevel' - if provided, a binary indicator of the significance to the
%                alpha level is returned for each feature in fv_rval.sigmask
%
% For compatibility PROC_AVERAGE can be called in the old format with CLASSES
% as second argument (which is now set via opt.Classes):
% CLASSES - classes of which the average is to be calculated,
%           names of classes (strings in a cell array), or 'ALL' (default)
%
%Returns:
% EPO     - updated data structure with fields
%  .x     - classwise means
%  .N     - vector of epochs per class across which average was calculated
%  .std   - standard deviation, if requested (opt.Std==1), format as epo.x
%  .se    - contains the standard error of the mean, if opt.Stats==1
%  .tstat     - Student t statistics of the difference, if opt.Stats==1
%  .df    - degrees of freedom of the t distribution (one sample test)
%  .p     - p value of null hypothesis that the mean is zero,
%           derived from t Statistics using two-sided test, if opt.Stats==1
%           If opt.Bonferroni==1, the p-value is multiplied by
%           epo.corrfac and cropped at 1.
%  .sgnlogp - contains the signed log10 p-value, if opt.Stats==1
%           if opt.Bonferroni==1, the p-value is multiplied by
%           epo.corrfac, cropped, and then logarithmized
%  .sigmask - binary array indicating significance at alpha level
%             opt.Alphalevel, if opt.Stats==1 and opt.Alphalevel > 0
%  .corrfac - Bonferroni correction factor (number of simultaneous tests),
%             if opt.Bonferroni==1
%  .crit    - 'significance' threshold of t statistics with respect to
%             level alpha
%
% Benjamin Blankertz
% 09-2012 stefan.haufe@tu-berlin.de
% 10-2015 Daniel Miklody

props= {  'Policy'   'mean' '!CHAR(mean nanmean median)';
          'Classes' 'ALL'   '!CHAR';
          'Std'      0      '!BOOL';
          'Stats'      0    '!BOOL';
          'Bonferroni' 0    '!BOOL';
          'Alphalevel' []   'DOUBLE'};

if nargin==0,
  out = props; return
end

misc_checkType(epo, 'STRUCT(x clab y)');
if nargin==2&&(iscellstr(varargin{1})||ischar(varargin{1}))
  opt.Classes = varargin{:};
else
  opt= opt_proplistToStruct(varargin{:});
end
[opt, isdefault]= opt_setDefaults(opt, props);
opt_checkProplist(opt, props);
epo = misc_history(epo);

%% delegate a special case:
if isfield(epo, 'yUnit') && isequal(epo.yUnit, 'dB'),
  out= proc_dBAverage(epo, varargin{:});
  return;
end

%%
classes = opt.Classes;

if ~isfield(epo, 'y'),
  warning('no classes label found: calculating average across all epochs');
  nEpochs= size(epo.x, ndims(epo.x));
  epo.y= ones(1, nEpochs);
  epo.className= {'all'};
end

if isequal(opt.Classes, 'ALL'),
  classes= epo.className;
end
if ischar(classes), classes= {classes}; end
if ~iscell(classes),
  error('classes must be given cell array (or string)');
end
nClasses= length(classes);

if max(sum(epo.y,2))==1,
  warning('only one epoch per class - nothing to average');
  out= proc_selectClasses(epo, classes);
  out.N= ones(1, nClasses);
  return;
end

out= epo;
%  clInd= find(ismember(epo.className, classes));
%% the command above would not keep the order of the classes in cell 'ev'
evInd= cell(1,nClasses);
for ic= 1:nClasses,
  clInd= find(ismember(epo.className, classes{ic},'legacy'));
  evInd{ic}= find(epo.y(clInd,:));
end

sz= size(epo.x);
out.x= zeros(prod(sz(1:end-1)), nClasses);
if opt.Std,
  out.std= zeros(prod(sz(1:end-1)), nClasses);
end
if opt.Stats,
  out.se = zeros(prod(sz(1:end-1)), nClasses);
  out.p = zeros(prod(sz(1:end-1)), nClasses);
  out.tstat = zeros(prod(sz(1:end-1)), nClasses);
  out.sgnlogp = zeros(prod(sz(1:end-1)), nClasses);
end
out.y= eye(nClasses);
out.className= classes;
out.N= zeros(1, nClasses);
epo.x= reshape(epo.x, [prod(sz(1:end-1)) sz(end)]);
for ic= 1:nClasses,
  switch(lower(opt.Policy)),  %% alt: feval(opt.Policy, ...)
   case 'mean',
    out.x(:,ic)= mean(epo.x(:,evInd{ic}), 2);
   case 'nanmean',
    out.x(:,ic)= nanmean(epo.x(:,evInd{ic}), 2);
   case 'median',
    warning('median computation will be handled by proc_percentiles in the future');
    out.x(:,ic)= median(epo.x(:,evInd{ic}), 2);
   otherwise,
    error('unknown policy');
  end
  if opt.Std,
    if strcmpi(opt.Policy,'nanmean'),
      out.std(:,ic)= nanstd(epo.x(:,evInd{ic}), 0, 2);
    else
      out.std(:,ic)= std(epo.x(:,evInd{ic}), 0, 2);
    end
  end
  out.N(ic)= length(evInd{ic});
  if opt.Stats,
    if strcmpi(opt.Policy,'nanmean'),
      [H out.p(:, ic) ci stats] = ttest(epo.x(:,evInd{ic}), [], [], [], 2);
      out.se(:,ic)= stats.sd/sqrt(out.N(ic));
    else
      [H out.p(:, ic) ci stats] = ttest(epo.x(:,evInd{ic}), [], [], [], 2);
      out.se(:,ic)= stats.sd/sqrt(out.N(ic));
    end
    out.tstat(:, ic) = stats.tstat;
    out.df(ic) = stats.df(1);
    if ~isempty(opt.Alphalevel)
      out.crit(ic) = stat_calcTCrit(opt.Alphalevel, stats.df(1));
    end
  end
end

out.x= reshape(out.x, [sz(1:end-1) nClasses]);
if opt.Std,
  out.std= reshape(out.std, [sz(1:end-1) nClasses]);
end

if opt.Stats,
  out.tstat= reshape(out.tstat, [sz(1:end-1) nClasses]);
  out.se = reshape(out.se, [sz(1:end-1) nClasses]);
  out.p = reshape(out.p, [sz(1:end-1) nClasses]);
  if opt.Bonferroni
    out.corrfac = prod(sz(1:end-1));
    out.p = min(out.p*out.corrfac, 1);
  end
  out.sgnlogp = -log10(out.p).*sign(out.x);
  if ~isempty(opt.Alphalevel)
    out.alphalevel = opt.Alphalevel;
    out.sigmask = out.p < opt.Alphalevel;
  end
end

out.indexedByEpochs = {};


%EDGSET Set Dirichlet edge constraints.
%
%   [ F, INDROW, AMAT, T ] = EDGSET( PROB, F, AMAT, ISYMM, SET_NULL )
%   Sets Dirichlet edge constraints conditions in the right hand side
%   load vector F and global matrix AMAT with the information in the finite
%   element problem struct PROB.
%
%       Input       Value/(Size)           Description
%       -----------------------------------------------------------------------------------
%       prob        struct                 Finite element problem struct
%       f           (neq,1)                Right hand side/load vector
%       amat        (n_a,n_a)              System matrix (sparse or triplet format)
%       isymm       scalar/{0}             Symmetrize BCs if applicable.
%       set_null    scalar/{0}             Set zeros in f vector.
%                                                                                .
%       Output      Value/(Size)           Description
%       -----------------------------------------------------------------------------------
%       f           (neq,1)                Modified right hand side/load vector
%       indrow      (neq,1)                Index to rows (dofs) in rhs which were set
%       amat        (n_a,n_a)              Modified system matrix
%       t           scalar                 Time spent in function

% Copyright 2013-2022 Precise Simulation, Ltd.


function [k, sk] = linKernCompute(kern, x, x2)

% LINKERNCOMPUTE Compute the LIN kernel given the parameters and X.
% FORMAT
% DESC computes the kernel parameters for the linear
% kernel given inputs associated with rows and columns.
% ARG kern : the kernel structure for which the matrix is computed.
% ARG x : the input matrix associated with the rows of the kernel.
% ARG x2 : the input matrix associated with the columns of the kernel.
% RETURN k : the kernel matrix computed at the given points.
%
% FORMAT
% DESC computes the kernel matrix for the linear
% kernel given a design matrix of inputs.
% ARG kern : the kernel structure for which the matrix is computed.
% ARG x : input data matrix in the form of a design matrix.
% RETURN k : the kernel matrix computed at the given points.
%
% SEEALSO : linKernParamInit, kernCompute, kernCreate, linKernDiagCompute
%
% COPYRIGHT : Neil D. Lawrence, 2004, 2005, 2006, 2009

% KERN

if nargin < 3
  sk = x*x';
else
  sk = x*x2';
end
k = sk*kern.variance;
if issparse(x)
  k = full(k);
end


function [] = selectStateSeqMinExpectedHamDist( jobID, taskIDs, MIN_ITER )
% Take all burned-in samples from multiple chains of MCMC
%   divide into Reference (3/4) and Test (1/4) sets
% Return the stateSeq from the Test set
%   that minimizes the expected hamming distance to all Reference samples

% jobID = 1121566;
% taskIDs = 1:5;
% MIN_ITER = 15000;
REF_FRAC = 9/10;

% ==================================================== Load Stored Samples
aa = 0;
for tt = 1:length( taskIDs )
    taskID = taskIDs(tt);
    X = loadSamplerOutput( jobID, taskID );
    for ss = 1:length( X.S )
       if X.iters.S(ss) <= MIN_ITER
           continue;
       end
       aa = aa+1;
       AllSamps(aa).stateSeq = X.S(ss).stateSeq;
       AllSamps(aa).taskID = taskID;
       AllSamps(aa).jobID  = jobID;
       AllSamps(aa).sampID = ss;
       AllSamps(aa).sampIter = X.iters.S(ss);
    end
end

nSamps = length( AllSamps );
permIDs = randperm( nSamps );
R = round( REF_FRAC * nSamps );
RefSamps = AllSamps( permIDs(1:R) );
TestSamps = AllSamps( permIDs(R+1:end)  );

fprintf( 'Searching for one test sample that best aligns to all reference samples\n');
fprintf( '  |Ref Set|=%d.  |Test Set|=%d\n', R, length(TestSamps)   );

MeanHamDist = zeros( 1, length( TestSamps ) );
tic;
for tt = 1:length( TestSamps )
    HDistToRefs = zeros( 1, R );
    for rr = 1:length( RefSamps )
        HDistToRefs(rr) = calcHammingDistance( RefSamps(rr).stateSeq,  TestSamps(tt).stateSeq );
    end
    MeanHamDist(tt) = mean( HDistToRefs );
    if tt==1 || mod( tt, 25 ) == 0 || tt == length(TestSamps)
        fprintf( ' %5.0f sec. %4d/%d test sequences examined\n', toc, tt, length(TestSamps) );
    end
end

[~, bestID] = min( MeanHamDist );

BestSample = TestSamps( bestID );
X = loadSamplerOutput( BestSample.jobID, BestSample.taskID );
BestSample.A = X.A( BestSample.sampID );

fid = fopen( '~/git/liv-video/SimulationResults.path' );
RESULTS_DIR = textscan( fid, '%s' );
RESULTS_DIR =RESULTS_DIR{1}{1};
fclose(fid);
outpath = fullfile( RESULTS_DIR, num2str(jobID), 'MinExpectedHamDistSample.mat' );
save( outpath, '-struct', 'BestSample' );
fprintf( '... wrote best sample to file %s\n', outpath );


function CPD = learn_params(CPD, fam, data, ns, cnodes)
%function CPD = learn_params(CPD, local_data)
% LEARN_PARAMS Compute the ML/MAP estimate of the params of a tabular CPD given complete data
% CPD = learn_params(CPD, local_data)
%
% local_data(i,m) is the value of i'th family member in case m (can be cell array).

local_data = data(fam, :);
if iscell(local_data)
  local_data = cell2num(local_data);
end
counts = compute_counts(local_data, CPD.sizes);
switch CPD.prior_type
 case 'none', CPD.CPT = mk_stochastic(counts);
 case 'dirichlet', CPD.CPT = mk_stochastic(counts + CPD.dirichlet);
 otherwise, error(['unrecognized prior ' CPD.prior_type])
end


% NMFLAB for Signal Processing written by A. Cichocki and R. Zdunek
% in cooperation with other members of Laboratory for Advanced Brain Signal
% Processing, BSI, RIKEN, Saitama, JAPAN

function [A,X,Distance_output]=nmf_conv(Y,r,Index_norm, A_true, S, mc_on, NoAlts,restart_mc_on,AL,Y_true,type_alg_A, type_alg_X, max_restart, no_conv)
%
% Non-negative Matrix Factorization (NMF) with convolutive algorithms
%
% [A,X]=nmf_IP(Y, r, Index_norm, A_true, S, mc_on, NoAlts, restart_mc_on, AL, Y_true, type_alg_A, type_alg_X, ...
%       max_restart, no_iter, alpha0, tau)
%       produces mixing matrix A of dimension [m by r],
%       and source matrix X of dimension [r by T], for the linear mixing model: AX = Y,
%       where Y is an observation matrix [m by T].
% Note: > m: number of sensors,
%       > r: number of sources,
%       > T: number of samples,
%
% INPUTS:
%       > Index_norm:    vector of 13 binary entries indicating which number
%                        divergence measures are turned on in the View Options,
%
%       > A_true:        true mixing matrix (only for synthetic data),
%       > S:             true source matrix (only for synthetic data),
%       > mc_on:         1 - Monte Carlo analysis enabled, 0 - otherwise,
%       > NoAlts:        number of alternating steps (only for Monte Carlo
%                        analysis and the option "Fixed Alternatings" is selected)
%       > restart_mc_on: 1 - restarts in Monte Carlo analysis are enabled,
%                        0 - otherwise,
%       > AL:            mixing matrix estimaed from the preceeding layer,
%       > Y_true:        the first layer mixed signals (mixtures),
%       > type_alg_A:    indicates the selected algorithm for computation
%                        of the mixing matrix,
%       > type_alg_X:    indicates the selected algorithm for computation of the sources,
%       > max_restart:   number of restarts,
%       > no_iter:       number of inner iterations,
%       > alpha0:        initial magnitude in the exponential model for regularization parameter,
%       > tau:           damping factor in the exponential model for regularization parameter,
%
% OUTPUTS:
%       > A:               estimated mixing matrix,
%       > X:               estimated source matrix,
%       > Distance_output: structures of different divergences measured between "Y" and estimated "AX" versus iterations,
%
% #########################################################################
A = [];
X = [];
if (nargin < 14) | isempty(no_conv) | (no_conv < 0) | max(size(no_conv) > 1)
   disp('Incorrect number of convolutive components');
   return
end
if (nargin < 13) | isempty(max_restart)  | (max_restart < 0) | max(size(max_restart) > 1)
   disp('Number of restarts must be given correctly');
   return
end
if (nargin < 12) | isempty(type_alg_X) | (type_alg_X < 1) | max(size(type_alg_X) > 1)
   disp('Incorrect algorithm for X');
   return
end
if (nargin < 11) | isempty(type_alg_A) | (type_alg_A < 1) | max(size(type_alg_A) > 1)
   disp('Incorrect algorithm for A');
   return
end
if (nargin < 10) | isempty(Y_true)
   disp('The first layer mixed signals are unknown');
   Y_true = zeros(size(Y_true));
end
if (nargin < 9) | isempty(AL)
   disp('Mixing matrix from the preceeding layer unknown');
   AL = eye(size(Y,1));
end
if (nargin < 8) | isempty(restart_mc_on) | max(size(restart_mc_on) > 1)
   disp('Index od restarts in MC analysis unknown');
   restart_mc_on = 0;
end
if (nargin < 7) | isempty(NoAlts) | max(size(NoAlts) > 1)
   disp('Adjustable number of alternatings');
   NoAlts = [];
end
if (nargin < 6) | isempty(mc_on) | max(size(mc_on) > 1)
   disp('No Monte Carlo Analysis');
   mc_on = 0;
end
if (nargin < 5) | isempty(S)
   disp('X_true not given');
end
if (nargin < 4) | isempty(A_true)
   disp('A_true not given');
   index_fixed_A = 1;
else
   index_fixed_A = 0;
end
if (nargin < 3) | isempty(Index_norm)
   '"Index_norm" must be specified'
   return
end
if (nargin < 2) | isempty(r)
   'Rank of factorization must be given'
   return
end

if isempty(Y) | isnan(Y)
   error('No data');
   return
end
% test for negative values in Y
if min(min(Y)) < 0
    disp('Some matrix entries are changed from negative to small positive');
    Y(Y< 0) = eps;
end
if min(sum(Y,2)) == 0
    disp('Not all entries in a row can be zero');
    return
end

[m,T]=size(Y);
niter_selected = 1000;     % maximum number of iterations for the selected sample (can be adjusted)
niter_sample = 30; % maximum number of iterations for each random sample
epsil_normA = 1E-14; % tolerance for alternating

if mc_on & ~restart_mc_on
    max_restart = 0;
end
if ~isempty(NoAlts)
    niter_selected = NoAlts;
end

if (type_alg_A == 3) & (size(A_true,1) ~= size(Y,1))
   disp('Multilayer technique cannot be used with A fixed');
   Distance_output = [];
   return
end

% Declaration for A and X
A=zeros(m,r,no_conv+1);
Ap = A;
X=zeros(r,T);
Ainit = A;
Xinit = X;
Z = zeros(m,T);
KL_outer_temp = 0;
Z_outer = 0;
nr = 0; restart_on = 0; norm_A = 10; nr_best = -1;
m_sx = 1:m; r_sx = 1:r; T_sx = 1:T;
s_dist = 0;
delta = .1;

while (nr <= max_restart)

   % Initialize random A and X
       if ~nr & (~mc_on | restart_mc_on)
          Ainit_1(m_sx',r_sx) = abs(repmat(.1*sin(2*pi*.1*m_sx'),1,r) + repmat(.1*cos(2*pi*.1*r_sx),m,1) + repmat(cos(2*pi*.471*m_sx'),1,r) + repmat(sin(2*pi*.471*r_sx),m,1));
          Ainit_1 = Ainit_1/max(max(Ainit_1));
          Ainit = repmat(Ainit_1,[1 1 no_conv+1]);

          Xinit(r_sx',T_sx) = abs(repmat(.1*sin(2*pi*.1*r_sx'),1,T) + repmat(.1*cos(2*pi*.1*T_sx),r,1) + repmat(cos(2*pi*.471*r_sx'),1,T) + repmat(sin(2*pi*.471*T_sx),r,1));
          Xinit = Xinit/max(max(Xinit));
       else
          Ainit=rand(m,r,no_conv+1);
          Xinit=rand(r,T);
       end

        % Normalization of initial guess
       Ainit = Ainit.*repmat((1./sum(Ainit,1)),[m,1,1]);

        if (nr == max_restart)&(max_restart > 0)
           A = A_best;
           X = X_best;
        else
           A = Ainit;
           X = Xinit;
        end % initial guess assignment

    Yx = zeros(m,T);
    n = 0; k = 0;

while ((k <= niter_sample)&(nr < max_restart)) | ((k <= niter_selected)&(nr == max_restart)&(norm_A > epsil_normA)& isempty(NoAlts)) | ((k <= niter_selected)&(nr == max_restart)& (NoAlts > 0))

k = k + 1;

l = mod(k-1,no_conv+1)+1;
%Xs = (circshift(X',l-1))';
Xs = X(:, (no_conv-l+2):(T-l+1));
Ys = Y(:, (no_conv-l+2):(T-l+1));

        switch type_alg_A

           case 1 % ALS

              Ap = A;
              Xpinv = Xs'*pinv(Xs*Xs');
              A(:,:,l) = max(1E6*eps, Ys*Xpinv);
              A = A.*repmat((1./sum(A,1)),[m,1,1]);

           case 2 % EMML

              Ap = A;
              A(:,:,l) = A(:,:,l).*((Y./(Yz + eps))*Xs')./repmat(sum(Xs,2)',m,1);
              A = A.*repmat((1./sum(A,1)),[m,1,1]);

           case 3 % Fixed A

              Ap = A;
              niter_selected = 1000;     % maximum number of iterations for the selected sample (can be adjusted)
              A = A_true + eps;
              A = A.*repmat((1./sum(A,1)),[m,1,1]);

        end % type_alg_A

        switch type_alg_X

            case 1 % ALS

               Xs = max(1E6*eps,pinv(squeeze(A(:,:,l))'*squeeze(A(:,:,l)))*squeeze(A(:,:,l))'*Ys);

            case 2 % EMML

              Zy = (circshift((Y./(Yz + eps))',-l+1))';
               if l > 1
                  Zy(:,(T-l+2):T) = zeros(m,l-1);
               end
              X = X.*(A(:,:,l)'*Zy)./repmat(sum(A(:,:,l),1)',1,T);

            case 3 % Fixed X

              X = S + eps;
              niter_selected = 1000;     % maximum number of iterations for the selected sample (can be adjusted)

        end % type_alg_X

      %  X = (circshift(Xs',-l+1))';

                if (nr == max_restart)&(mod(k,50)==0)& (~mc_on | restart_mc_on)
                   norm_A = sqrt(sum(sum(sum((A - Ap).^2))));
                   fprintf(1, 'Restart %d,  %d-th alternating step\n',nr_best+1,k);
                end

                if sum(Index_norm)
                   if (nr == max_restart) & (((k < 50) & (mod(k,5)==0)) | ((k>49) & ((mod(k,50)==0))))

                       s_dist = s_dist + 1;
                       k_select(s_dist) = k;
                       Z = A*X + eps;
                       Z = diag(1./sqrt(var(Z')))*Z;
                       Y = Y + eps;

                        dist_Fro(s_dist) = norm(Y - Z,'fro');
                        dist_KL(s_dist) = sum(sum(Y.*log(Y./Z + eps) - Y + Z));
                        dist_KL2(s_dist) = sum(sum(Z.*log(Z./Y + eps) + Y - Z));
                        dist_Pearson(s_dist) = sum(sum( ((Y - Z).^2)./Z ));
                        dist_Hellinger(s_dist) = sum(sum( (sqrt(Z) - sqrt(Y)).^2 ));
                        dist_JS_rel(s_dist) = sum(sum(2*Y.*log(2*Y./(Y + Z) + eps) + Z - Y));
                        dist_JS_rel2(s_dist) = sum(sum(2*Z.*log(2*Z./(Y + Z) + eps) - Z + Y));
                        Zy = Y + Z;
                        dist_JS(s_dist) = sum(sum(Y.*log(2*Y./Zy + eps) + Z.*log(2*Z./Zy + eps) ));
                        dist_AG_rel(s_dist) = sum(sum(Zy.*log(.5*Zy./Y + eps) + Y - Z));
                        dist_AG(s_dist) = sum(sum(.5*Zy.*log(.5*Zy./sqrt(Y.*Z) + eps)));
                        dist_J(s_dist) = sum(sum( .5*(Y - Z).*log(Y./Z + eps) ));
                        dist_Chi(s_dist) = sum(sum( ((Y + Z).*(Y - Z).^2)./(Y.*Z) ));
                        dist_Tria(s_dist) = sum(sum( ((Y - Z).^2)./(Y + Z) ));

                    end % if multiple
                end % if sum

end % while (k)

% Outer KL divergence
if ndims(A) > 2
   Yz = zeros(m,T);
   for t = 1:size(A,3)
       Ytmp = squeeze(A(:,:,t))*X;
       Yz = Yz + Ytmp;
   end
   Z = AL*Yz;
else
    Z = AL*A*X;
end
Z_outer = norm(Z,'fro') + eps;
KL_outer = sum(sum(Y_true.*log((Y_true + eps)./(Z + eps)) - Y_true + Z))/Z_outer;

           if (nr == 0) | (KL_outer < KL_outer_temp)
              A_best = A; X_best = X; KL_outer_temp = KL_outer; nr_best = nr;
           end % multi-conditions

   nr = nr + 1;

   if nr <=max_restart
      fprintf(1, 'Restart %d, Kullback-Leibler divergence = %e\n',	nr, KL_outer);
   end

end % while (restarts)

% One-Variance scaling
X(X <= 0) = eps;

if ndims(A) > 2
   A =  sum(A,3);
end

Distance_output = cell(length(s_dist),1);
Distance_output(1) = {[]};
Distance_output(2) = {[]};
Distance_output(3) = {[]};
Distance_output(4) = {[]};
Distance_output(5) = {[]};
Distance_output(6) = {[]};
Distance_output(7) = {[]};
Distance_output(8) = {[]};
Distance_output(9) = {[]};
Distance_output(10) = {[]};
Distance_output(11) = {[]};
Distance_output(12) = {[]};
Distance_output(13) = {[]};
Distance_output(14) = {[]};

if sum(Index_norm)
   Distance_output(1) = {k_select};
   Distance_output(2) = {dist_Fro};
   Distance_output(3) = {dist_KL};
   Distance_output(4) = {dist_KL2};
   Distance_output(5) = {dist_Pearson};
   Distance_output(6) = {dist_Hellinger};
   Distance_output(7) = {dist_JS_rel};
   Distance_output(8) = {dist_JS_rel2};
   Distance_output(9) = {dist_JS};
   Distance_output(10) = {dist_AG_rel};
   Distance_output(11) = {dist_AG};
   Distance_output(12) = {dist_J};
   Distance_output(13) = {dist_Chi};
   Distance_output(14) = {dist_Tria};
end


function aRatio = computePerClassAspectRatios(posImgDir, posGtDir, pLoad, aRatioType, plot_orientation_dist)

if nargin<5
  plot_orientation_dist = 0;
end

% JMBUENA!!
% Compute the average window size for every subclass of the
% positive metaclass. We are going to keep this average window size
% within the detector classifier. We will train the detector with
% fixed big window (selecting the features in this window) but we
% finally return the "per class" best fit window.
tid  = ticStatus('Per class aspect ratio',1,30);
fs={posImgDir,posGtDir};
fs=bbGt('getFiles',fs); nImg=size(fs,2); assert(nImg>0);
gt   = cell(nImg,1);
lbls = cell(nImg,1);
pLoad2=pLoad;
%if iscell(pLoad)
%  pLoad2 = pLoad{:};
%end

use_mean_aratio = 0;
if iscell(pLoad)
  index = find(strcmp(pLoad2, 'squarify'));
  if ~isempty(index)
    pLoad2{index+1} = {};
  end
elseif isstruct(pLoad2)
  if isfield(pLoad2, 'squarify')
    pLoad2.squarify = {};
  end
end

for i=1:nImg
  [objs_,gt_] = bbGt('bbLoad',fs{2,i},pLoad2);
  indices = gt_(:,5)==0;
  if (sum(indices)>0)
    lbls{i} = [objs_(indices).subclass]';
    gt{i}   = gt_(indices,:);
  end
  tocStatus(tid,i/nImg);
end
gt   = cell2mat(gt);
lbls = cell2mat(lbls);
num_pos_classes = length(unique(lbls));
ratios = gt(:,3)./gt(:,4);
aRatio = zeros(num_pos_classes, 1);
for i=1:num_pos_classes
  if (strcmp(aRatioType, 'mean') == 1)
    aRatio(i) = mean(ratios(lbls==i));
  else %(strcmp(aRatioType, 'median') == 1)
    aRatio(i) = median(ratios(lbls==i));
  end
%  aRatio(i) = prctile(ratios(lbls==i),60);
%   a_ratio_class_std(i) = std(ratios(lbls==i));
end

if plot_orientation_dist
  % Plot orientation distributios
  for i=1:num_pos_classes
    figure;
    haxes = axes;
    data_i = ratios(lbls == i);
    [h, x] = hist(data_i,50);
  %  hf = h ./ length(data_i);
    stairs(x, h, 'g-');
    axis([0 4 0 max(h)]);
    mean_i = mean(data_i);
    median_i = median(data_i);
    hold on;
    line([mean_i, mean_i], get(haxes, 'YLim'), 'Color', [1 0 0]);
    line([median_i, median_i], get(haxes, 'YLim'), 'Color', [1 0 1]);
    hold off;
    title(sprintf('orientation %d', i));
  end;
end;


function D = cluster_discrim(cl,beh,varargin)
% D = cluster_discrim(cl,beh,[covs of no interest])
%
%x = cat(2,pain_matrix_early(:).timeseries);
%x = cat(2,pain_matrix_peak(:).timeseries);

diary Cluster_Discrim_Output.txt

disp('cluster_discrim.m: Predictions of behavior with contrast scores');
fprintf(1,'\n-----------------------------------------------------------------\n');

disp(['Input clusters: ' inputname(1)])
disp('Loading data from cl.timeseries');

% Get values from clusters
% ---------------------------------------------
x = cat(2,cl(:).timeseries);
if any(isnan(sum(x))),
    whnan = find(isnan(sum(x)));
    cl(whnan) = []; x = cat(2,cl(:).timeseries);
    disp(['Found NaNs! Removing clusters ' num2str(whnan)]);
end

% Remove covariates of no interest
% ---------------------------------------------
covs = [];
if length(varargin) > 0
    covs = varargin{1};
    D.covs = covs;
    disp('Found covariate(s) of no interest; Removing them from behavior and brain data.')
        % partialcorr gets adjusted x, y, and correls; here just use it to
        % get adjusted x
        beh  = partialcor([beh covs],ones(size(beh,1),1),1);

    for i = 1:size(x,2)
        x(:,i) = partialcor([x(:,i) covs],ones(size(x,1),1),1);
    end
end

D.data = x;

figure;imagesc(x); colorbar; title('Data for discriminant analysis'); drawnow

D.xyzmm = cat(1,cl(:).mm_center);

y = beh;
D.beh = beh;

y2 = mediansplit(y);
D.beh_medsplit = y2;

if isfield(cl,'imnames'),D.imnames = cl(1).imnames;,end

% Print univariate correlations with regions and location (t-test)
% ---------------------------------------------
disp('Correlations with behavior, after removing covariates of no interest.')
fprintf(1,'%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n','Cluster','Name','x','y','z','V','r', 'p', 'IRLSr', 'IRLSp','Zavg','Inc/Dec');
warning off
fprintf(1,'\n');
for i = 1:size(x,2)
    [dummy,dummy,r,p,rrob,prob]  = partialcor([beh covs],x(:,i),1);

     % print output
     fprintf(1,'Cluster %3.0f\t',i);
     if isfield(cl,'shorttitle'), fprintf(1,'%s\t',cl(i).shorttitle),end
     if isfield(cl,'mm_center'),fprintf(1,'%3.0f\t%3.0f\t%3.0f\t',cl(i).mm_center);,end
     if isfield(cl,'numVox'),fprintf(1,'%3.0f\t',cl(i).numVox);,end
     fprintf(1,'%3.2f\t%3.2f\t%3.2f\t%3.2f\t',[r p rrob prob])

     % t-test
     [hh,p,ci,stats] = ttest(cl(i).timeseries);
     strs = {'Decrease' '--' 'Increase'};
     if ~hh, strs = strs{2};,elseif stats.tstat >0,strs=strs{3};,else,strs = strs{1};end
     fprintf(1,'%3.2f\t%s\t',stats.tstat,strs)

     fprintf(1,'\n');
end
warning on

% --------------------------------------
% get eigenvalues, choose # dims
% --------------------------------------
disp('Computing significant principal components')
[v,score,e] = princomp(x);
[pc,D.pca_npm] = pca_npm(x,1000);
whsig = find(cumsum(D.pca_npm.sig) >= 1:length(D.pca_npm.sig));
ndim = length(whsig) + 1;

try, saveas(gcf,'Cluster_eigenplot','tif');,end

% --------------------------------------
% predict behavior with component scores
% --------------------------------------

fprintf(1,'\n-----------------------------------------------------------------\n');
fprintf(1,'Stepwise regression: Predictions of behavior with component scores');
fprintf(1,'\n-----------------------------------------------------------------\n');

D.PCR = stepwise_tor(score(:,1:ndim),y);

sig = find(D.PCR.inmodel);
D.PCR.sscore = score(:,sig);    % scores for significant components

% plot all significant (now done in cluster_discrim_montage)
%figure('Color','w');
%for i = 1:size(D.PCR.sscore,2)
%    subplot(1,size(D.PCR.sscore,2),i);
%    discrim_plot(D.PCR.sscore(:,i),y,0);
%end

D.PCR.seigv = v(:,sig);         % eigenvectors for sig components
D.PCR.seig = e(sig);            % significant eigenvalues

tmpc = corrcoef([D.PCR.sscore x]);
tmpc(1:size(D.PCR.sscore,2),:) = []; tmpc = tmpc(:,1:size(D.PCR.sscore,2));
D.PCR.compcorr = tmpc;
D.PCR.compcorr_descrip = 'Regions x sig. comps, correlation with component values';
D.PCR.whichcomps = sign(D.PCR.compcorr) .* real(abs(D.PCR.compcorr) > .4);

tmpc(abs(tmpc) < .4) = 0;
D.PCR.threshcomps = tmpc;

% --------------------------------------
% manova to get categorical canonical discrim functions
% this gives us overall p-value and discrim functions
% --------------------------------------
[manovasig,manovap,D.manova] = manova1(score(:,1:ndim),y2);
D.manova.p = manovap;
fprintf(1,'\n-----------------------------------------------------------------\n');
fprintf(1,'Manova to discriminate high from low behavior');
fprintf(1,'\n-----------------------------------------------------------------\n');
fprintf(1,'Wilks lambda (%3.0f,%3.0f) = %3.2f\tChi-sq (%3.0f) = %3.2f\t, p = %3.4f\t\n',D.manova.dfB,D.manova.dfW,D.manova.lambda,D.manova.chisqdf,D.manova.chisq,D.manova.p);
%discrim_plot(D.manova.canon(:,1),y);
fprintf(1,'Eigenvalues\t')
fprintf(1,'%3.2f\t',D.manova.eigenval')
fprintf(1,'\n')
fprintf(1,'Eigenvector 1\t')
fprintf(1,'%3.2f\t',D.manova.eigenvec(:,1)')
fprintf(1,'\n')

% --------------------------------------
% visualize clusters on brain
% --------------------------------------

cluster_discrim_montage(cl,D.PCR.threshcomps,D.PCR.sscore,D.beh);

% --------------------------------------
% manova on individual regions??
% --------------------------------------

diary off

return


function [inference] = tapas_sem_hier_inference(inference, pars)
%%
%
% aponteeduardo@gmail.com
% copyright (C) 2016
%

if ~isfield(inference, 'estimate_method')
    inference.estimate_method = @tapas_mcmc_blocked_estimate;
end

if ~isfield(inference, 'initialize_states')
    inference.initialize_states = @tapas_sem_hier_init_states;
end

if ~isfield(inference, 'initialize_state')
    inference.initialize_state = @tapas_sem_hier_init_state;
end

if ~isfield(inference, 'sampling_methods')
    inference.sampling_methods = {
        @(d, m, i, s) tapas_mh_mc3_tempering_sample_node(d, m, i, s, 2), ...
        @(d, m, i, s) tapas_sampler_dlinear_gibbs_node(d, m, i, s, 3), ...
        @tapas_sampler_mc3_tempering ...
        ...  Use population mcmc step with generalized
        % Temperature for possibly Bayesian predictive distribution.
        };
end

if ~isfield(inference, 'metasampling_methods')
    inference.metasampling_methods = {@tapas_mcmc_meta_diagnostics, ...
        @tapas_mcmc_meta_adaptive, ...
%        @tapas_mcmc_meta_adaptive_ti, ...
        };
end

if ~isfield(inference, 'get_stored_state')
    inference.get_stored_state = @tapas_sem_hier_get_stored_state;
end

if ~isfield(inference, 'prepare_posterior')
    inference.prepare_posterior = @tapas_sem_hier_prepare_posterior;
end

if ~isfield(inference, 'mh_sampler')
    inference.mh_sampler = cell(4, 1);
end

if ~isfield(inference.mh_sampler{2}, 'propose_sample')
    inference.mh_sampler{2}.propose_sample = ...
        @tapas_mh_mc3_propose_gaussian_sample;
    inference.mh_sampler{2}.ar_rule = ...
        @tapas_mh_mc3g_arc;
end

if ~isfield(inference, 'niter')
    if isfield(pars, 'niter')
        inference.niter = pars.niter;
    else
        inference.niter = 5000;
    end
end

if ~isfield(inference, 'nburnin')
    if isfield(pars, 'nburnin')
        inference.nburnin = pars.nburnin;
    else
        inference.nburnin = 5000;
    end
end

if ~isfield(inference, 'mc3it')
    if isfield(pars, 'mc3it')
        inference.mc3it = pars.mc3it;
    else
        inference.mc3it = 10;
    end
end

if ~isfield(inference, 'thinning')
    if isfield(pars, 'thinning')
        inference.thinning = pars.thinning;
    else
        inference.thinning = 0;
    end
end

if ~isfield(inference, 'ndiag')
    if isfield(pars, 'ndiag')
        inference.ndiag = pars.ndiag;
    else
        inference.ndiag = 200;
    end
end

% Scale of the kernel
if ~isfield(inference, 'kernel_scale')
    inference.kernel_scale = 0.1 * 0.1;
end

end


%% Make plots
n_plot = floor(20/dt);
subplot(2,3,1);
hold on;
plot(vTime(1:n_plot),monetary_shock(1:n_plot),'linewidth',1.5,'linestyle',line_style,'color',color);
set(gcf,'color','w');
title('Monetary Policy Shock','interpreter','latex','fontsize',14);
ylabel('$\%$ deviation','interpreter','latex');
xlim([1 vTime(n_plot)]);
grid on;
hold off;

subplot(2,3,2);
hold on;
plot(vTime(1:n_plot),inflation(1:n_plot),'linewidth',1.5,'linestyle',line_style,'color',color);
set(gcf,'color','w');
title('Inflation','interpreter','latex','fontsize',14);
ylabel('$\%$ deviation','interpreter','latex');
xlim([1 vTime(n_plot)]);
grid on;
hold off;

subplot(2,3,3);
hold on;
plot(vTime(1:n_plot),consumption(1:n_plot),'linewidth',1.5,'linestyle',line_style,'color',color);
set(gcf,'color','w');
title('Consumption','interpreter','latex','fontsize',14);
ylabel('$\%$ deviation','interpreter','latex');
xlim([1 vTime(n_plot)]);
grid on;
hold off;

subplot(2,3,4);
hold on;
plot(vTime(1:n_plot),Y(1:n_plot),'linewidth',1.5,'linestyle',line_style,'color',color);
set(gcf,'color','w');
title('GDP','interpreter','latex','fontsize',14);
ylabel('$\%$ deviation','interpreter','latex');
xlim([1 vTime(n_plot)]);
grid on;
hold off;

subplot(2,3,5);
hold on;
plot(vTime(1:n_plot),lab_sup(1:n_plot),'linewidth',1.5,'linestyle',line_style,'color',color);
set(gcf,'color','w');
title('Labor Supply','interpreter','latex','fontsize',14);
ylabel('$\%$ deviation','interpreter','latex');
xlim([1 vTime(n_plot)]);
grid on;
hold off;

subplot(2,3,6);
hold on;
plot(vTime(1:n_plot),wage(1:n_plot),'linewidth',1.5,'linestyle',line_style,'color',color);
set(gcf,'color','w');
title('Wage','interpreter','latex','fontsize',14);
ylabel('$\%$ deviation','interpreter','latex');
xlim([1 vTime(n_plot)]);
grid on;
hold off;


function [fBValue, fAValue, fStdDev, fMc] = plot_FMD2(mCatalog, hAxes, rCalculateMC, rDisplay)
% function [fBValue, fAValue, fStdDev, fMc]
%   = plot_FMD2(mCatalog, bCumulative, hAxes, sSymbol, sColor, bPlotB, nCalculateMC, fBinning)
% --------------------------------------------------------------------------------------------
% Creates and plots a frequency magnitude distribution including the b-value
%
% plot_FMD2(mCatalog) opens a figure and plots the frequency magnitude distribution
%   with standard parameters
%
% Input parameters:
%   mCatalog        Earthquake catalog
%   hAxes           Handle of axes to plot the frequency magnitude distribution
%   rCalculateMC    Record with information about magnitude of completeness computing
%     nMethod       Method to determine the magnitude of completeness (see calc_Mc) (default 1)
%     bConstrain    Constrain Mc to a given range [fMinMc; fMaxMc] (default 0)
%     fMinMc        Minimum Mc when constrained
%     fMaxMc        Maximum Mc when constrained
%     fBinning      Magnitude binning of the catalog (default 0.1)
%   rDisplay        Record with display preferences
%     sSymbol       Symbol for FMD (default 's')
%     sColor        Color of FMD (default 'k')
%     bPlotB        Plot b-value line (=1) or not (=0) (default 1)
%     bCumulative   Plot cumulative frequency magnitude distribution (=1) or non-cumulative (=0)
%
% Output parameters:
%   fBValue         Calculated b-value
%   fAValue         Calculateda-value
%   fStdDev         Standard deviation of b-value
%   fMc             Magnitude of completeness
%
% Danijel Schorlemmer
% November 5, 2003

global bDebug;
if bDebug
  report_this_filefun(mfilename('fullpath'));
end

% Create the frequency magnitude distribution vector
[vFMD, vNonCFMD] = calc_FMD(mCatalog);

% Define missing input parameters
if ~exist('hAxes', 'var')
  figure;
  hAxes = newplot;
end
if ~exist('rCalculateMC', 'var')
  rCalculateMC.nMethod = 1;
  rCalculateMC.bConstrain = 0;
  rCalculateMC.fMinMc = -10;
  rCalculateMC.fMaxMc = 10;
  rCalculateMC.fBinning = 0.1;
end
if ~exist('rDisplay', 'var')
  rDisplay.sSymbol = 's';
  rDisplay.sColor = 'k';
  rDisplay.bPlotB = 1;
  rDisplay.bCumulative = 1;
end

% Activate given axes
axes(hAxes);
set(hAxes, 'NextPlot', 'add');

% Plot the frequency magnitude distribution
if rDisplay.bCumulative
  hPlot = semilogy(vFMD(1,:), vFMD(2,:), [rDisplay.sSymbol rDisplay.sColor]);
else
  hPlot = semilogy(vNonCFMD(1,:), vNonCFMD(2,:), [rDisplay.sSymbol rDisplay.sColor]);
end
set(hAxes, 'YScale', 'log'); % Bug in Matlab

if rDisplay.bPlotB
  % Calculate magnitude of completeness
  [fBValue, vDummy, fMc, fAValue] = calc_BandMc(mCatalog, 0, rCalculateMC.nMethod, rCalculateMC.fBinning, rCalculateMC.bConstrain, rCalculateMC.fMinMc, rCalculateMC.fMaxMc);

  % Determine the positions of 'x'-markers
  nIndexLo = find((vFMD(1,:) < fMc + (rCalculateMC.fBinning/2)) & (vFMD(1,:) > fMc - (rCalculateMC.fBinning/2)));
  fMagHi = vFMD(1,1);
  vSel = vFMD(1,:) <= fMagHi & vFMD(1,:) >= fMc-.0001;
  vMagnitudes = vFMD(1,vSel);

  % Plot the 'x'-marker
  hPlot = semilogy(vFMD(1,nIndexLo), vFMD(2,nIndexLo), ['x' rDisplay.sColor]);
  set(hPlot, 'LineWidth', [2], 'MarkerSize', 12);
  hPlot = semilogy(vFMD(1,1), vFMD(2,1), ['x' rDisplay.sColor]);
  set(hPlot, 'LineWidth', [2], 'MarkerSize', 12)

  % Plot the line representing the b-value
  vPoly = [-1*fBValue fAValue];
  fBFunc = 10.^(polyval(vPoly, vMagnitudes));
  hPlot = semilogy(vMagnitudes, fBFunc, rDisplay.sColor);
  set(hPlot, 'LineWidth', [2]);
end


function pass = test_constructor_turbo(pref)

% NB:  This test checks basic construction syntax only.  Accuracy testing is
% done in the corresponding test in tests/chebtech.

% Get preferences.
if ( nargin < 1 )
    pref = chebfunpref();
end

% Check that the right number of coefficients were calculated.
f_plain = chebfun(@exp, pref);
f_turbo = chebfun(@exp, 'turbo');
pass(1) = length(f_turbo) == 2*length(f_plain);

% Try an array-valued example.
f_plain = chebfun(@(x) [exp(x) 1./(x + 5)], pref);
f_turbo = chebfun(@(x) [exp(x) 1./(x + 5)], 'turbo');
pass(2) = length(f_turbo) == 2*length(f_plain);

% Try specifying a number of coefficients.
f = chebfun(@exp, 75, 'turbo');
pass(3) = length(f) == 75;

% Try specifying a number of coefficients for an array-valued input.
f = chebfun(@(x) [exp(x) 1./(x + 5)], 75, 'turbo');
pass(4) = length(f) == 75;

% Check that things work for constructions with breakpoints.
f_plain = chebfun(@exp, [-1 0 1], pref);
f_turbo = chebfun(@exp, [-1 0 1], 'turbo');
pass(5) = length(f_turbo) == 2*length(f_plain);

f_plain = chebfun(@(x) exp(x).*sign(x), 'splitting', 'on', pref);
f_turbo = chebfun(@(x) exp(x).*sign(x), 'splitting', 'on', 'turbo');
pass(6) = length(f_turbo) == 2*length(f_plain);

% Check that things work for constructions with exponents.
f_plain = chebfun(@(x) sin(x).*sqrt(1 + x), 'exps', [0.5 0], pref);
f_turbo = chebfun(@(x) sin(x).*sqrt(1 + x), 'exps', [0.5 0], 'turbo');
pass(7) = length(f_turbo) == 2*length(f_plain);

end


%% DEMO_febio_0077_cube_perfect_osmometer_01.m
% Below is a demonstration for:
%
% * Building geometry for a cube with hexahedral elements
% * Defining the boundary conditions
% * Coding the febio structure
% * Running the model
% * Importing and visualizing the displacement and stress results

%% Keywords
%
% * febio_spec version 3.0
% * febio, FEBio
% * perfect osmometer
% * hexahedral elements, hex8
% * cube, box, rectangular
% * static, solid
% * displacement logfile
% * stress logfile

%%

clear; close all; clc;

%% Plot settings
fontSize=20;
faceAlpha1=0.8;
markerSize=40;
markerSize2=35;
lineWidth=3;
cMap=viridis(250); %colormap

%% Control parameters

% Path names
defaultFolder = fileparts(fileparts(mfilename('fullpath')));
savePath=fullfile(defaultFolder,'data','temp');

% Defining file names
febioFebFileNamePart='tempModel';
febioFebFileName=fullfile(savePath,[febioFebFileNamePart,'.feb']); %FEB file name
febioLogFileName=[febioFebFileNamePart,'.txt']; %FEBio log file name
febioLogFileName_disp=[febioFebFileNamePart,'_disp_out.txt']; %Log file name for exporting displacement
febioLogFileName_vol=[febioFebFileNamePart,'_vol_out.txt']; %Log file name for exporting stress
febioLogFileName_stress_prin=[febioFebFileNamePart,'_stress_prin_out.txt']; %Log file name for exporting principal stress

%Specifying dimensions and number of elements
cubeSize=1;
sampleWidth=cubeSize; %Width
sampleThickness=cubeSize; %Thickness
sampleHeight=cubeSize; %Height
pointSpacings=0.3*ones(1,3); %Desired point spacing between nodes
numElementsWidth=round(sampleWidth/pointSpacings(1)); %Number of elemens in dir 1
numElementsThickness=round(sampleThickness/pointSpacings(2)); %Number of elemens in dir 2
numElementsHeight=round(sampleHeight/pointSpacings(3)); %Number of elemens in dir 3

%Material parameter set
E_youngs=1;
v_pois=0.3;

iosm=300;
bosm_ini=300;
bosm_diff_amp=200;

% FEA control settings
numTimeSteps=50; %Number of time steps desired
max_refs=25; %Max reforms
max_ups=0; %Set to zero to use full-Newton iterations
opt_iter=12; %Optimum number of iterations
max_retries=5; %Maximum number of retires
dtmin=(1/numTimeSteps)/100; %Minimum time step size
dtmax=1/numTimeSteps; %Maximum time step size
runMode='internal';

%% Creating model geometry and mesh
% A box is created with tri-linear hexahedral (hex8) elements using the
% |hexMeshBox| function. The function offers the boundary faces with
% seperate labels for the top, bottom, left, right, front, and back sides.
% As such these can be used to define boundary conditions on the exterior.

% Create a box with hexahedral elements
cubeDimensions=[sampleWidth sampleThickness sampleHeight]; %Dimensions
cubeElementNumbers=[numElementsWidth numElementsThickness numElementsHeight]; %Number of elements
outputStructType=2; %A structure compatible with mesh view
[meshStruct]=hexMeshBox(cubeDimensions,cubeElementNumbers,outputStructType);

%Access elements, nodes, and faces from the structure
E=meshStruct.elements; %The elements
V=meshStruct.nodes; %The nodes (vertices)
Fb=meshStruct.facesBoundary; %The boundary faces
Cb=meshStruct.boundaryMarker; %The "colors" or labels for the boundary faces
elementMaterialIndices=ones(size(E,1),1); %Element material indices

%%
% Plotting model boundary surfaces and a cut view

hFig=cFigure;

subplot(1,2,1); hold on;
title('Model boundary surfaces and labels','FontSize',fontSize);
gpatch(Fb,V,Cb,'k',faceAlpha1);
colormap(gjet(6)); icolorbar;
axisGeom(gca,fontSize);

hs=subplot(1,2,2); hold on;
title('Cut view of solid mesh','FontSize',fontSize);
optionStruct.hFig=[hFig hs];
meshView(meshStruct,optionStruct);
axisGeom(gca,fontSize);

drawnow;

%% Defining the boundary conditions
% The visualization of the model boundary shows colors for each side of the
% cube. These labels can be used to define boundary conditions.

%Define supported node sets
logicFace=Cb==1; %Logic for current face set
Fr=Fb(logicFace,:); %The current face set
bcSupportList_X=unique(Fr(:)); %Node set part of selected face

logicFace=Cb==3; %Logic for current face set
Fr=Fb(logicFace,:); %The current face set
bcSupportList_Y=unique(Fr(:)); %Node set part of selected face

logicFace=Cb==5; %Logic for current face set
Fr=Fb(logicFace,:); %The current face set
bcSupportList_Z=unique(Fr(:)); %Node set part of selected face

%Prescribed displacement nodes
logicPrescribe=Cb==6; %Logic for current face set
Fr=Fb(logicPrescribe,:); %The current face set
bcPrescribeList=unique(Fr(:)); %Node set part of selected face

%%
% Visualizing boundary conditions. Markers plotted on the semi-transparent
% model denote the nodes in the various boundary condition lists.

hf=cFigure;
title('Boundary conditions','FontSize',fontSize);
xlabel('X','FontSize',fontSize); ylabel('Y','FontSize',fontSize); zlabel('Z','FontSize',fontSize);
hold on;

gpatch(Fb,V,'kw','k',0.5);

hl(1)=plotV(V(bcSupportList_X,:),'r.','MarkerSize',markerSize);
hl(2)=plotV(V(bcSupportList_Y,:),'g.','MarkerSize',markerSize);
hl(3)=plotV(V(bcSupportList_Z,:),'b.','MarkerSize',markerSize);
hl(4)=plotV(V(bcPrescribeList,:),'k.','MarkerSize',markerSize);

legend(hl,{'BC x support','BC y support','BC z support','BC z prescribe'});

axisGeom(gca,fontSize);
camlight headlight;
drawnow;

%% Defining the FEBio input structure
% See also |febioStructTemplate| and |febioStruct2xml| and the FEBio user
% manual.

%Get a template with default settings
[febio_spec]=febioStructTemplate;

%Set globals/constants
febio_spec.Globals.Constants.R=8.314e-6;
febio_spec.Globals.Constants.T=310;

%febio_spec version
febio_spec.ATTR.version='3.0';

%Module section
febio_spec.Module.ATTR.type='biphasic';

febio_spec.Output.plotfile.var{end+1}.ATTR.type='fluid pressure';
febio_spec.Output.plotfile.var{end+1}.ATTR.type='effective fluid pressure';

%Control section
febio_spec.Control.analysis='STATIC';
febio_spec.Control.time_steps=numTimeSteps;
febio_spec.Control.step_size=1/numTimeSteps;
febio_spec.Control.solver.max_refs=max_refs;
febio_spec.Control.solver.max_ups=max_ups;
febio_spec.Control.time_stepper.dtmin=dtmin;
febio_spec.Control.time_stepper.max_retries=max_retries;
febio_spec.Control.time_stepper.opt_iter=opt_iter;

%Use must-points by specifying
febio_spec.Control.time_stepper=rmfield(febio_spec.Control.time_stepper,'dtmax'); %Remove existing template dtmax definition
febio_spec.Control.time_stepper.dtmax.ATTR.lc=1; %Set load curve id for dtmax
febio_spec.Control.time_stepper.dtmax.VAL=1; %Set value

%Material section
materialName1='Material1';
febio_spec.Material.material{1}.ATTR.name=materialName1;
febio_spec.Material.material{1}.ATTR.type='solid mixture';
febio_spec.Material.material{1}.ATTR.id=1;
febio_spec.Material.material{1}.mat_axis.ATTR.type='vector';
febio_spec.Material.material{1}.mat_axis.a=[1 0 0];
febio_spec.Material.material{1}.mat_axis.d=[0 1 0];

febio_spec.Material.material{1}.solid{1}.ATTR.type='perfect osmometer';
febio_spec.Material.material{1}.solid{1}.phiw0=0.8;
febio_spec.Material.material{1}.solid{1}.iosm=iosm;
febio_spec.Material.material{1}.solid{1}.bosm.ATTR.lc=2;
febio_spec.Material.material{1}.solid{1}.bosm.VAL=1;

febio_spec.Material.material{1}.solid{2}.ATTR.type='natural neo-Hookean';
febio_spec.Material.material{1}.solid{2}.E=E_youngs;
febio_spec.Material.material{1}.solid{2}.v=v_pois;

% Mesh section
% -> Nodes
febio_spec.Mesh.Nodes{1}.ATTR.name='Object1'; %The node set name
febio_spec.Mesh.Nodes{1}.node.ATTR.id=(1:size(V,1))'; %The node id's
febio_spec.Mesh.Nodes{1}.node.VAL=V; %The nodel coordinates

% -> Elements
partName1='Part1';
febio_spec.Mesh.Elements{1}.ATTR.name=partName1; %Name of this part
febio_spec.Mesh.Elements{1}.ATTR.type='hex8'; %Element type
febio_spec.Mesh.Elements{1}.elem.ATTR.id=(1:1:size(E,1))'; %Element id's
febio_spec.Mesh.Elements{1}.elem.VAL=E; %The element matrix

% -> NodeSets
nodeSetName1='bcSupportList_X';
nodeSetName2='bcSupportList_Y';
nodeSetName3='bcSupportList_Z';
nodeSetName4='bcPrescribeList';

febio_spec.Mesh.NodeSet{1}.ATTR.name=nodeSetName1;
febio_spec.Mesh.NodeSet{1}.node.ATTR.id=bcSupportList_X(:);

febio_spec.Mesh.NodeSet{2}.ATTR.name=nodeSetName2;
febio_spec.Mesh.NodeSet{2}.node.ATTR.id=bcSupportList_Y(:);

febio_spec.Mesh.NodeSet{3}.ATTR.name=nodeSetName3;
febio_spec.Mesh.NodeSet{3}.node.ATTR.id=bcSupportList_Z(:);

%MeshDomains section
febio_spec.MeshDomains.SolidDomain.ATTR.name=partName1;
febio_spec.MeshDomains.SolidDomain.ATTR.mat=materialName1;

%Boundary condition section
% -> Fix boundary conditions
febio_spec.Boundary.bc{1}.ATTR.type='fix';
febio_spec.Boundary.bc{1}.ATTR.node_set=nodeSetName1;
febio_spec.Boundary.bc{1}.dofs='x';

febio_spec.Boundary.bc{2}.ATTR.type='fix';
febio_spec.Boundary.bc{2}.ATTR.node_set=nodeSetName2;
febio_spec.Boundary.bc{2}.dofs='y';

febio_spec.Boundary.bc{3}.ATTR.type='fix';
febio_spec.Boundary.bc{3}.ATTR.node_set=nodeSetName3;
febio_spec.Boundary.bc{3}.dofs='z';

%LoadData section
% -> load_controller
febio_spec.LoadData.load_controller{1}.ATTR.id=1;
febio_spec.LoadData.load_controller{1}.ATTR.type='loadcurve';
febio_spec.LoadData.load_controller{1}.interpolate='STEP';
febio_spec.LoadData.load_controller{1}.points.point.VAL=[0 dtmax; 0.25 dtmax; 0.5 dtmax; 0.75 dtmax; 1 dtmax;]; %Defines must-points

febio_spec.LoadData.load_controller{2}.ATTR.id=2;
febio_spec.LoadData.load_controller{2}.ATTR.type='loadcurve';
febio_spec.LoadData.load_controller{2}.interpolate='LINEAR';
febio_spec.LoadData.load_controller{2}.points.point.VAL=[0 bosm_ini; 0.25 bosm_ini+bosm_diff_amp; 0.5 bosm_ini; 0.75 bosm_ini-bosm_diff_amp; 1 bosm_ini;];

%Output section
% -> log file
febio_spec.Output.logfile.ATTR.file=febioLogFileName;
febio_spec.Output.logfile.node_data{1}.ATTR.file=febioLogFileName_disp;
febio_spec.Output.logfile.node_data{1}.ATTR.data='ux;uy;uz';
febio_spec.Output.logfile.node_data{1}.ATTR.delim=',';

febio_spec.Output.logfile.element_data{1}.ATTR.file=febioLogFileName_vol;
febio_spec.Output.logfile.element_data{1}.ATTR.data='J';
febio_spec.Output.logfile.element_data{1}.ATTR.delim=',';

febio_spec.Output.logfile.element_data{2}.ATTR.file=febioLogFileName_stress_prin;
febio_spec.Output.logfile.element_data{2}.ATTR.data='s1;s2;s3';
febio_spec.Output.logfile.element_data{2}.ATTR.delim=',';

%% Quick viewing of the FEBio input file structure
% The |febView| function can be used to view the xml structure in a MATLAB
% figure window.

%%
% |febView(febio_spec); %Viewing the febio file|

%% Exporting the FEBio input file
% Exporting the febio_spec structure to an FEBio input file is done using
% the |febioStruct2xml| function.

febioStruct2xml(febio_spec,febioFebFileName); %Exporting to file and domNode
%system(['gedit ',febioFebFileName,' &']);

%% Running the FEBio analysis
% To run the analysis defined by the created FEBio input file the
% |runMonitorFEBio| function is used. The input for this function is a
% structure defining job settings e.g. the FEBio input file name. The
% optional output runFlag informs the user if the analysis was run
% succesfully.

febioAnalysis.run_filename=febioFebFileName; %The input file name
febioAnalysis.run_logname=febioLogFileName; %The name for the log file
febioAnalysis.disp_on=1; %Display information on the command window
febioAnalysis.runMode=runMode;
febioAnalysis.maxLogCheckTime=10; %Max log file checking time

[runFlag]=runMonitorFEBio(febioAnalysis);%START FEBio NOW!!!!!!!!

%% Import FEBio results

if runFlag==1 %i.e. a succesful run

    %%

    % Importing nodal displacements from a log file
    dataStruct=importFEBio_logfile(fullfile(savePath,febioLogFileName_disp),1,1);

    %Access data
    N_disp_mat=dataStruct.data; %Displacement
    timeVec=dataStruct.time; %Time

    %Create deformed coordinate set
    V_DEF=N_disp_mat+repmat(V,[1 1 size(N_disp_mat,3)]);

    %%
    % Plotting the simulated results using |anim8| to visualize and animate
    % deformations

    DN_magnitude=sqrt(sum(N_disp_mat(:,:,end).^2,2)); %Current displacement magnitude

    % Create basic view and store graphics handle to initiate animation
    hf=cFigure; %Open figure
    gtitle([febioFebFileNamePart,': Press play to animate']);
    title('Displacement magnitude [mm]','Interpreter','Latex')
    hp=gpatch(Fb,V_DEF(:,:,end),DN_magnitude,'k',1,2); %Add graphics object to animate
    hp.Marker='.';
    hp.MarkerSize=markerSize2;
    hp.FaceColor='interp';
    gpatch(Fb,V,0.5*ones(1,3),'none',0.25); %A static graphics object

    axisGeom(gca,fontSize);
    colormap(cMap); colorbar;
    caxis([0 max(DN_magnitude)]); caxis manual;
    axis(axisLim(V_DEF)); %Set axis limits statically
    view(140,30);
    camlight headlight;

    % Set up animation features
    animStruct.Time=timeVec; %The time vector
    for qt=1:1:size(N_disp_mat,3) %Loop over time increments
        DN_magnitude=sqrt(sum(N_disp_mat(:,:,qt).^2,2)); %Current displacement magnitude

        %Set entries in animation structure
        animStruct.Handles{qt}=[hp hp]; %Handles of objects to animate
        animStruct.Props{qt}={'Vertices','CData'}; %Properties of objects to animate
        animStruct.Set{qt}={V_DEF(:,:,qt),DN_magnitude}; %Property values for to set in order to animate
    end
    anim8(hf,animStruct); %Initiate animation feature
    drawnow;

    %%
    % Importing element stress from a log file
    dataStruct=importFEBio_logfile(fullfile(savePath,febioLogFileName_vol),1,1);

    %Access data
    E_J_mat=dataStruct.data;

    %%
    % Plotting the simulated results using |anim8| to visualize and animate
    % deformations

    [CV]=faceToVertexMeasure(E,V,E_J_mat(:,:,end));

    % Create basic view and store graphics handle to initiate animation
    hf=cFigure; %Open figure  /usr/local/MATLAB/R2020a/bin/glnxa64/jcef_helper: symbol lookup error: /lib/x86_64-linux-gnu/libpango-1.0.so.0: undefined symbol: g_ptr_array_copy

    gtitle([febioFebFileNamePart,': Press play to animate']);
    title('$J$ [.]','Interpreter','Latex')
    hp=gpatch(Fb,V_DEF(:,:,end),CV,'k',1,2); %Add graphics object to animate
    hp.Marker='.';
    hp.MarkerSize=markerSize2;
    hp.FaceColor='interp';
    gpatch(Fb,V,0.5*ones(1,3),'none',0.25); %A static graphics object

    axisGeom(gca,fontSize);
    colormap(cMap); colorbar;
    caxis([min(E_J_mat(:)) max(E_J_mat(:))]);
    axis(axisLim(V_DEF)); %Set axis limits statically
    view(140,30);
    camlight headlight;

    % Set up animation features
    animStruct.Time=timeVec; %The time vector
    for qt=1:1:size(N_disp_mat,3) %Loop over time increments

        [CV]=faceToVertexMeasure(E,V,E_J_mat(:,:,qt));

        %Set entries in animation structure
        animStruct.Handles{qt}=[hp hp]; %Handles of objects to animate
        animStruct.Props{qt}={'Vertices','CData'}; %Properties of objects to animate
        animStruct.Set{qt}={V_DEF(:,:,qt),CV}; %Property values for to set in order to animate
    end
    anim8(hf,animStruct); %Initiate animation feature
    drawnow;

    %%
%     % Importing element stresses from a log file
%     dataStruct=importFEBio_logfile(fullfile(savePath,febioLogFileName_stress_prin),1,1);
%
%     %Access data
%     E_stress_prin_mat=dataStruct.data;
%
%     %Compute pressure
%     P = squeeze(sum(sum(E_stress_prin_mat,1),2));%squeeze(-1/3*mean(sum(E_stress_prin_mat,2),1));
%
%     %%
%     % Visualize pressure-stretch curve
%
%     cFigure; hold on;
%     title('Pressure-time curve','FontSize',fontSize);
%     xlabel('$t$ [s]','FontSize',fontSize,'Interpreter','Latex');
%     ylabel('$p$ [MPa]','FontSize',fontSize,'Interpreter','Latex');
%
%     plot(timeVec(:),P(:),'r-','lineWidth',lineWidth);
%
%     view(2); axis tight;  grid on; axis square; box on;
%     set(gca,'FontSize',fontSize);
%     drawnow;

end

%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>

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


function sparse_test ( )

%*****************************************************************************80
%
%% SPARSE_TEST tests the SPARSE library.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    13 April 2014
%
%  Author:
%
%    John Burkardt
%
  timestamp ( );
  fprintf ( 1, '\n' );
  fprintf ( 1, 'SPARSE_TEST\n' );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '  Test MATLAB''s sparse matrix features.\n' );

  sparse_test01 ( );
  sparse_test02 ( );
  sparse_test03 ( );
  sparse_test04 ( );
  sparse_test05 ( );
  sparse_test06 ( );
  sparse_test07 ( );
%
%  Terminate.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'SPARSE_TEST\n' );
  fprintf ( 1, '  Normal end of execution.\n' );
  fprintf ( 1, '\n' );
  timestamp ( );

  return
end


function plot_frame(pt,R)
framesize = 1;
x_axis = R(:,1)*framesize;
y_axis = R(:,2)*framesize;
z_axis = R(:,3)*framesize;
plot3([pt(1) x_axis(1)+pt(1)],[pt(2) x_axis(2)+pt(2)], [pt(3) x_axis(3)+pt(3)], 'b')
%hold on
plot3([pt(1) y_axis(1)+pt(1)],[pt(2) y_axis(2)+pt(2)], [pt(3) y_axis(3)+pt(3)], 'g')
plot3([pt(1) z_axis(1)+pt(1)],[pt(2) z_axis(2)+pt(2)], [pt(3) z_axis(3)+pt(3)], 'r')
%hold off


function [n_covered,covering_boxes, covering_ids, covered_boxes, uncovered_boxes, object_ids]=computeNCoveredBoxes(ranked_boxes,ground_truth_boxes,n_windows, unique_solution,overlap_criterion)
n_g=size(ground_truth_boxes,1);
n_covered=zeros(length(n_windows),1);
n_windows=[0 n_windows];
object_ids=[];
nDets=size(ranked_boxes,1);

covering_ids=[];
covered_ids=[];
uncovered_ids=[];
if(unique_solution)
  n_windows(n_windows>nDets)=nDets;
  already_found = zeros(size(ground_truth_boxes,1),1);
  for k=2:length(n_windows)
    for i_r=(n_windows(k-1)+1):n_windows(k)
      for i_g=1:n_g
        if(~already_found(i_g))
          pascalScore = computePascalScore(ranked_boxes(i_r,:),ground_truth_boxes(i_g,:));
          if(pascalScore > overlap_criterion)
            n_covered(k-1)=n_covered(k-1)+1;
            covering_ids=[covering_ids;i_r];
            covered_ids=[covered_ids;i_g];
            already_found(i_g)=1;
          end
        end
      end
    end
  end
  object_ids=(1:size(ground_truth_boxes,1))';
else
  for k=2:length(n_windows)
    for i_r=(n_windows(k-1)+1):n_windows(k)
      for i_g=1:n_g
        pascalScore = computePascalScore(ranked_boxes(i_r,:),ground_truth_boxes(i_g,:));
        if(pascalScore > overlap_criterion)
          n_covered(k-1)=n_covered(k-1)+1;
          covering_ids=[covering_ids;i_r];
          covered_ids=[covered_ids;i_g];
          object_ids=[object_ids;i_g];
        end
      end
    end
  end
end

uncovered_ids = setxor(covered_ids,1:size(ground_truth_boxes,1));

covering_boxes=ranked_boxes(covering_ids,:);
covered_boxes=ground_truth_boxes(covered_ids,:);
uncovered_boxes=ground_truth_boxes(uncovered_ids,:);

n_covered=cumsum(n_covered);
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spherical Harmonic Modeling and Analysis Toolkit (SPHARM-MAT) is a 3D
% shape modeling and analysis toolkit.
% It is a software package developed at Shenlab in Center for Neuroimaging,
% Indiana University (SpharmMat@gmail.com, http://www.iupui.edu/~shenlab/)
% It is available to the scientific community as copyright freeware
% under the terms of the GNU General Public Licence.
%
% Copyright 2009, 2010, ShenLab, Center for Neuroimaging, Indiana University
%
% This file is part of SPHARM-MAT.
%
% SPHARM-MAT is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% SPHARM-MAT is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with SPHARM-MAT. If not, see <http://www.gnu.org/licenses/>.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function displayStat(confs, objs, method, windowHandle, cpath)

load(deblank(char(objs)));

[p,fname,e] = fileparts(deblank(char(objs)));

switch lower(method)
    case 'res_t_map'
        switch deblank(char(confs.Overlay))
            case 'p-value'
                cutoff = confs.Threshold_p_value;
                signal = pvalue;
            case 't-map'
                df = length(grInfo)-1;
                cutoff = tinv(1-(confs.Threshold_p_value/2),df);
                signal = tstats;
        end
        patch_overlay(atlas_vertices, faces, signal, cutoff, deblank(char(confs.Overlay)), vtnorm, deblank(char(confs.Colormap)),windowHandle,fname);
    case 'res_pca'
        project_PCA(fvecs, eigenvecs, eigenvals, deblank(char(confs.Mesh)), confs.MaxSPHARMDegree, confs.Level, confs.Sigma, windowHandle,fname, cpath);
end

return;


function [SE_MR,SE_RZF,SE_MMMSE,SE_ZF,SE_SMMSE] = functionComputeSE_DL_hardening(H,Hhat,C,R,tau_c,tau_p,nbrOfRealizations,M,K,L,p,rho)
%Compute DL SE for different transmit precoding schemes using Theorem 4.6.
%
%INPUT:
%H                 = M x nbrOfRealizations x K x L x L matrix with the
%                    exact channel realizations
%Hhat              = M x nbrOfRealizations x K x L x L matrix with the
%                    channel estimates
%C                 = M x M x K x L x L matrix with estimation error
%                    correlation matrices
%R                 = M x M x K x L x L matrix with spatial correlation
%                    matrices
%tau_c             = Length of coherence block
%tau_p             = Length of pilot sequences
%nbrOfRealizations = Number of channel realizations
%M                 = Number of antennas per BS
%K                 = Number of UEs per cell
%L                 = Number of BSs and cells
%p                 = Uplink transmit power per UE (same for everyone)
%rho               = Downlink transmit power per UE (same for everyone)
%
%OUTPUT:
%SE_MR    = K x L x length(tau_c) matrix where element (k,l,:) is the
%           downlink SE of UE k in cell l achieved with MR precoding for
%           different lengths of the coherence block
%SE_RZF   = Same as SE_MR but with RZF precoding
%SE_MMMSE = Same as SE_MR but with M-MMSE precoding
%SE_ZF    = Same as SE_MR but with ZF precoding
%SE_SMMSE = Same as SE_MR but with S-MMSE precoding
%
%
%This Matlab function was developed to generate simulation results to:
%
%Emil Bjornson, Jakob Hoydis and Luca Sanguinetti (2017),
%"Massive MIMO Networks: Spectral, Energy, and Hardware Efficiency",
%Foundations and Trends in Signal Processing: Vol. 11, No. 3-4,
%pp. 154-655. DOI: 10.1561/2000000093.
%
%For further information, visit: https://www.massivemimobook.com
%
%This is version 1.01 (Last edited: 2020-05-15)
%
%License: This code is licensed under the GPLv2 license. If you in any way
%use this code for research that results in publications, please cite our
%monograph as described above.

%Store identity matrices of different sizes
eyeK = eye(K);
eyeM = eye(M);

if nargout > 2

    %Compute sum of all estimation error correlation matrices at every BS
    C_totM = reshape(p*sum(sum(C,3),4),[M M L]);

end

%Compute sum of intra-cell estimation error correlation matrices at every BS
if nargout>4

    CR_totS = zeros(M,M,L);

    for j = 1:L
        CR_totS(:,:,j) = p*(sum(C(:,:,:,j,j),3)+sum(sum(R(:,:,:,[1:j-1 j+1:end],j),3),4));
    end

end

%Compute the prelog factor assuming only downlink transmission
prelogFactor = (tau_c-tau_p)./(tau_c);

%Prepare to store simulation results for signal gains
signal_MR = zeros(K,L);

if nargout > 1
    signal_RZF = zeros(K,L);
end

if nargout > 2
    signal_MMMSE = zeros(K,L);
end

if nargout > 3
    signal_ZF = zeros(K,L);
end

if nargout > 4
    signal_SMMSE = zeros(K,L);
end

%Prepare to store simulation results for sum interference powers
interf_MR = zeros(K,L);

if nargout > 1
    interf_RZF = zeros(K,L);
end

if nargout > 2
    interf_MMMSE = zeros(K,L);
end

if nargout > 3
    interf_ZF = zeros(K,L);
end

if nargout > 4
    interf_SMMSE = zeros(K,L);
end

%% Go through all channel realizations
for n = 1:nbrOfRealizations

    %Go through all cells
    for j = 1:L

        %Extract channel realizations from all UEs to BS j
        Hallj = reshape(H(:,n,:,:,j),[M K*L]);

        %Extract channel realizations from all UEs to BS j
        Hhatallj = reshape(Hhat(:,n,:,:,j),[M K*L]);

        %Compute MR combining in (4.11)
        V_MR = Hhatallj(:,K*(j-1)+1:K*j);

        if nargout > 1 %Compute RZF combining in (4.9)
            V_RZF = (p*V_MR)/(p*(V_MR'*V_MR)+eyeK);
        end

        if nargout > 2 %Compute M-MMSE combining in (4.7)
            V_MMMSE = (p*(Hhatallj*Hhatallj')+C_totM(:,:,j)+eyeM)\(p*V_MR);
        end

        if nargout > 3 %Compute ZF combining in (4.10), with the small regularization term 1e-12 for numerical stability
            V_ZF = V_MR/(V_MR'*V_MR+1e-12*eyeK);
        end

        if nargout > 4 %Compute S-MMSE combining in (4.8)
            V_SMMSE = (p*(V_MR*V_MR')+CR_totS(:,:,j)+eyeM)\(p*V_MR);
        end

        %Go through all UEs in cell j
        for k = 1:K

            if norm(V_MR(:,k))>0

                %%MR precoding in (4.37) computed based on combining vector
                w = V_MR(:,k)/norm(V_MR(:,k));

                %Compute realizations of the terms inside the expectations
                %of the signal and interference terms of (4.26)
                signal_MR(k,j) = signal_MR(k,j) + (w'*H(:,n,k,j,j))/nbrOfRealizations;
                interf_MR = interf_MR + rho*reshape(abs(w'*Hallj).^2,[K L])/nbrOfRealizations;

                %%RZF precoding
                if nargout > 1

                    %RZF precoding in (4.37) computed based on combining vector
                    w = V_RZF(:,k)/norm(V_RZF(:,k));

                    %Compute realizations of the terms inside the expectations
                    %of the signal and interference terms of (4.26)
                    signal_RZF(k,j) = signal_RZF(k,j) + (w'*H(:,n,k,j,j))/nbrOfRealizations;
                    interf_RZF = interf_RZF + rho*reshape(abs(w'*Hallj).^2,[K L])/nbrOfRealizations;

                end

                %%M-MMSE precoding
                if nargout > 2

                    %M-MMSE precoding in (4.37) computed based on combining vector
                    w = V_MMMSE(:,k)/norm(V_MMMSE(:,k));

                    %Compute realizations of the terms inside the expectations
                    %of the signal and interference terms of (4.26)
                    signal_MMMSE(k,j) = signal_MMMSE(k,j) + (w'*H(:,n,k,j,j))/nbrOfRealizations;
                    interf_MMMSE = interf_MMMSE + rho*reshape(abs(w'*Hallj).^2,[K L])/nbrOfRealizations;

                end

                %%ZF precoding
                if nargout > 3

                    %ZF precoding in (4.37) computed based on combining vector
                    w = V_ZF(:,k)/norm(V_ZF(:,k));

                    %Compute realizations of the terms inside the expectations
                    %of the signal and interference terms of (4.26)
                    signal_ZF(k,j) = signal_ZF(k,j) + (w'*H(:,n,k,j,j))/nbrOfRealizations;
                    interf_ZF = interf_ZF + rho*reshape(abs(w'*Hallj).^2,[K L])/nbrOfRealizations;

                end

                %%S-MMSE precoding
                if nargout > 4

                    %S-MMSE precoding in (4.37) computed based on combining vector
                    w = V_SMMSE(:,k)/norm(V_SMMSE(:,k));

                    %Compute realizations of the terms inside the expectations
                    %of the signal and interference terms of (4.26)
                    signal_SMMSE(k,j) = signal_SMMSE(k,j) + (w'*H(:,n,k,j,j))/nbrOfRealizations;
                    interf_SMMSE = interf_SMMSE + rho*reshape(abs(w'*Hallj).^2,[K L])/nbrOfRealizations;

                end

            end

        end

    end

end

%% Prepare to compute the SEs with different pilot reuse factors
SE_MR = zeros(K,L,length(tau_c));

if nargout > 1
    SE_RZF = zeros(K,L,length(tau_c));
end

if nargout > 2
    SE_MMMSE = zeros(K,L,length(tau_c));
end

if nargout > 3
    SE_ZF = zeros(K,L,length(tau_c));
end

if nargout > 4
    SE_SMMSE = zeros(K,L,length(tau_c));
end

%Go through all lengths of the pilot reuse factors
for n = 1:length(tau_c)

    %Compute SEs according to Theorem 4.6
    SE_MR(:,:,n) = prelogFactor(n)*real(log2(1+(rho*abs(signal_MR).^2) ./ (interf_MR - rho*abs(signal_MR).^2 + 1)));

    if nargout > 1
        SE_RZF(:,:,n) = prelogFactor(n)*real(log2(1+(rho*abs(signal_RZF).^2) ./ (interf_RZF - rho*abs(signal_RZF).^2 + 1)));
    end

    if nargout > 2
        SE_MMMSE(:,:,n) = prelogFactor(n)*real(log2(1+(rho*abs(signal_MMMSE).^2) ./ (interf_MMMSE - rho*abs(signal_MMMSE).^2 +1)));
    end

    if nargout > 3
        SE_ZF(:,:,n) = prelogFactor(n)*real(log2(1+(rho*abs(signal_ZF).^2) ./ (interf_ZF - rho*abs(signal_ZF).^2 + 1)));
    end

    if nargout > 4
        SE_SMMSE(:,:,n) = prelogFactor(n)*real(log2(1+(rho*abs(signal_SMMSE).^2) ./ (interf_SMMSE - rho*abs(signal_SMMSE).^2 + 1)));
    end

end