function DIC_3Djoined_results = join3DreconstructedPairs(DIC3DAllPairsResults)
%% function for joining multiple 3D-DIC pair results into one structure (plotting in step3)
%
% Inputs:
% * DIC3DAllPairsResults
%
% Outputs:
% * DIC_3Djoined_results
%

%%
nPairs=numel(DIC3DAllPairsResults);
nFrames=size(DIC3DAllPairsResults{1}.Points3D,1);

% create cells with all the variables for joining
Fi=cell(nPairs,1);
FCi=cell(nPairs,1);
Vi=cell(nPairs,nFrames);
corrCombi=cell(nPairs,nFrames);
FaceCorrCombi=cell(nPairs,nFrames);
DispVi=cell(nPairs,nFrames);
DispMi=cell(nPairs,nFrames);
VFci=cell(nPairs,nFrames);

for ip=1:nPairs
    DIC_3Dpair_struct=DIC3DAllPairsResults{ip};

    Fi{ip}=DIC_3Dpair_struct.Faces;
    FCi{ip}=DIC_3Dpair_struct.FaceColors;

    for it=1:nFrames
        Vi{ip,it}=DIC_3Dpair_struct.Points3D{it};
        corrCombi{ip,it}=DIC_3Dpair_struct.corrComb{it};
        FaceCorrCombi{ip,it}=DIC_3Dpair_struct.FaceCorrComb{it};
        DispVi{ip,it}=DIC_3Dpair_struct.Disp.DispVec{it};
        DispMi{ip,it}=DIC_3Dpair_struct.Disp.DispMgn{it};
        VFci{ip,it}=DIC_3Dpair_struct.FaceCentroids{it};
    end

end

% join variables that are time invariant
[Fj,~,~]=joinElementSets(Fi,Vi(:,1),[]);
[~,~,FCj]=joinElementSets(Fi,Vi(:,1),FCi);

for it=1:nFrames
    % join variables that are time variant and scalar

    [~,Vj{1,it},DispMj{1,it}]=joinElementSets(Fi,Vi(:,it),DispMi(:,it));
    [~,~,corrCombj{1,it}]=joinElementSets(Fi,Vi(:,it),corrCombi(:,it));
    [~,~,FaceCorrCombj{1,it}]=joinElementSets(Fi,Vi(:,it),FaceCorrCombi(:,it));

    % join variables that are time variant and vectors
    DispVj{1,it}=DispVi{1,it};
    VFcj{1,it}=VFci{1,it};

    for ip=2:nPairs
        DispVj{1,it}=[DispVi{1,it}; DispVi{ip,it}];
        VFcj{1,it}=[VFcj{1,it}; VFci{ip,it}];
    end
end

DIC_3Djoined_results=struct;
DIC_3Djoined_results.Faces=Fj;
DIC_3Djoined_results.FaceColors=FCj;
DIC_3Djoined_results.FaceCentroids=VFcj;
DIC_3Djoined_results.Points3D=Vj;
DIC_3Djoined_results.corrComb=corrCombj;
DIC_3Djoined_results.FaceCorrComb=FaceCorrCombj;

DIC_3Djoined_results.Disp.DispVec=DispVj;
DIC_3Djoined_results.Disp.DispMgn=DispMj;

end

%%
% MultiDIC: a MATLAB Toolbox for Multi-View 3D Digital Image Correlation
%
% License: <https://github.com/MultiDIC/MultiDIC/blob/master/LICENSE.txt>
%
% Copyright (C) 2018  Dana Solav
%
% If you use the toolbox/function for your research, please cite our paper:
% <https://engrxiv.org/fv47e>


function state = isintvector(var)

if ~isempty(var) & ...
   isreal(var) & ...
   isequal(var,fix(var)) & ...
   ndims(var) == 2 & ...
   ~(size(var,1)>1 & size(var,2)>1)
   state = 1;
else
   state = 0;
end


function keep(varargin);
%KEEP keeps the caller workspace variables of your choice and clear the rest.
%       Its usage is just like "clear" but only for variables.
%
%       Xiaoning (David) Yang   xyang@lanl.gov 1998
%       Revision based on comments from Michael McPartland,
%       michael@gaitalf.mgh.harvard.edu, 1999

%       Keep all
if isempty(varargin)
        return
end

%       See what are in caller workspace
wh = evalin('caller','who');

%       Check workspace variables
if isempty(wh)
        error('  There is nothing to keep!')
end

%       Construct a string containing workspace variables delimited by ":"
variable = [];
for i = 1:length(wh)
        variable = [variable,':',wh{i}];
end
variable = [variable,':'];

%       Extract desired variables from string
flag = 0;
for i = 1:length(varargin)
        I = findstr(variable,[':',varargin{i},':']);
        if isempty(I)
                disp(['       ',varargin{i}, ' does not exist!'])
                flag = 1;
        elseif I == 1
                variable = variable(1+length(varargin{i})+1:length(variable));
        elseif I+length(varargin{i})+1 == length(variable)
                variable = variable(1:I);
        else
                variable = [variable(1:I),variable(I+length(varargin{i})+2:length(variable))];
        end
end

%       No delete if some input variables do not exist
if flag == 1
        disp('       No variables are deleted!')
        return
end

%       Convert string back to cell and delete the rest
I = findstr(variable,':');
if length(I) ~= 1
        for i = 1:length(I)-1
                if i ~= length(I)-1
                        del(i) = {[variable(I(i)+1:I(i+1)-1),' ']};
                else
                        del(i) = {variable(I(i)+1:length(variable)-1)};
                end
        end
        evalin('caller',['clear ',del{:}])
end


% TRIAL2EEGPLOT - convert eeglab format to eeplot format of rejection window
%
% Usage:
%   >> eegplotarray = trial2eegplot(rej, rejE, points, color);
%
% Inputs:
%   rej   - rejection vector (0 and 1) with one value per trial
%   rejE  - electrode rejection array (size nb_elec x trials) also
%           made of 0 and 1.
%   points - number of points per trials
%   color  - color of the windows for EEGPLOT
%
% Outputs:
%   eegplotarray - array defining windows which is compatible with
%                  the function EEGPLOT
%
% Author: Arnaud Delorme, CNL / Salk Institute, 2001
%
% See also: EEGTRESH, EEGLAB, EEGPLOT, POP_REJEPOCH

% Copyright (C) 2001 Arnaud Delorme, Salk Institute, arno@salk.edu
%
% 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 rejeegplot = trial2eegplot( rej, rejE, pnts, color)
	rej  = find(rej>0);
	rejE = rejE(:, rej)';
   	rejeegplot = zeros(length(rej), size(rejE,2)+5);
   	rejeegplot(:, 6:end) = rejE;
   	rejeegplot(:, 1) = (rej(:)-1)*pnts;
   	rejeegplot(:, 2) = rej(:)*pnts-1;
   	rejeegplot(:, 3:5) = ones(size(rejeegplot,1),1)*color;
return


classdef EvolveProcessCapture
    % Shows the evolution process of spectral unmixing algorithms.
    properties
      ScatterOpts
      AbundOpts
      FramesScatter
      FramesAbund
    end
    methods
        function obj = EvolveProcessCapture()
        end

        function obj = begin(obj,max_iter)
            scatter_opts = [];
            scatter_opts.hs = [];
            scatter_opts.update = 0;

            abund_opts = [];
            abund_opts.imhs = [];
            abund_opts.update = 0;

            frames_scatter(max_iter) = struct('cdata',[],'colormap',[]);
            frames_abund(max_iter) = struct('cdata',[],'colormap',[]);

            obj.ScatterOpts = scatter_opts;
            obj.AbundOpts = abund_opts;
            obj.FramesScatter = frames_scatter;
            obj.FramesAbund = frames_abund;
        end

        function obj = end(obj,end_iter)
            obj.FramesScatter = obj.FramesScatter(1:end_iter);
            obj.FramesAbund = obj.FramesAbund(1:end_iter);
        end

        function obj = updateEndmVar(obj,Y,A,rows,cols,w_jk,mu_jk,sigma_jk,names,iter)
            obj = updateScatterPlotEndmVar(obj,Y,w_jk,mu_jk,sigma_jk,names,iter);
            obj = updateAbundanceMaps(obj,A,rows,cols,iter);

            drawnow
        end

        function obj = updateEndmFixed(obj,Y,A,rows,cols,R,names,iter)
            obj = updateScatterPlotEndmFixed(obj,Y,R,names,iter);
            obj = updateAbundanceMaps(obj,A,rows,cols,iter);

            drawnow
        end

        function obj = updateScatterPlotEndmVar(obj,Y,w_jk,mu_jk,sigma_jk,names,iter)
            scatter_opts = obj.ScatterOpts;
            frames_scatter = obj.FramesScatter;

            [~,hs,axes_h,scatter_fh] = endmember_scatter_plot_end_var( ...
                Y,w_jk,mu_jk,sigma_jk,names,scatter_opts);
            title(axes_h, ['Scatter plot of iteration ',num2str(iter)]);
            frames_scatter(iter) = getframe(scatter_fh);

            if ~scatter_opts.update
                pos = get(scatter_fh,'Position');
                pos(1) = pos(1) - pos(3) / 2 - 1;
                set(scatter_fh,'Position',pos);
            end
            scatter_opts.update = 1;
            scatter_opts.hs = hs;
            scatter_opts.axes_h = axes_h;
            scatter_opts.fig_h = scatter_fh;

            obj.ScatterOpts = scatter_opts;
            obj.FramesScatter = frames_scatter;
        end

        function obj = updateScatterPlotEndmFixed(obj,Y,R,names,iter)
            scatter_opts = obj.ScatterOpts;
            frames_scatter = obj.FramesScatter;

            [~,hs,axes_h,scatter_fh] = endmember_scatter_plot( ...
                Y,R,names,scatter_opts);
            title(axes_h, ['Scatter plot of iteration ',num2str(iter)]);
            frames_scatter(iter) = getframe(scatter_fh);

            if ~scatter_opts.update
                pos = get(scatter_fh,'Position');
                pos(1) = pos(1) - pos(3) / 2 - 1;
                set(scatter_fh,'Position',pos);
            end
            scatter_opts.update = 1;
            scatter_opts.hs = hs;
            scatter_opts.axes_h = axes_h;
            scatter_opts.fig_h = scatter_fh;

            obj.ScatterOpts = scatter_opts;
            obj.FramesScatter = frames_scatter;
        end

        function obj = updateAbundanceMaps(obj,A,rows,cols,iter)
            abund_opts = obj.AbundOpts;
            frames_abund = obj.FramesAbund;

            [imhs,abund_fh] = show_abundances(A,rows,cols,'',0,0,abund_opts);
            set(abund_fh, 'name', ['Abundances of iteration ',num2str(iter)]);
            frames_abund(iter) = getframe(abund_fh);

            if ~abund_opts.update
                pos = get(abund_fh,'Position');
                pos(1) = pos(1) + pos(3) / 2 + 1;
                set(abund_fh,'Position',pos);
            end
            abund_opts.update = 1;
            abund_opts.imhs = imhs;
            abund_opts.fig_h = abund_fh;

            obj.AbundOpts = abund_opts;
            obj.FramesAbund = frames_abund;
        end
    end
end


% Ryan Steindl based on Robotics Toolbox for MATLAB (v6 and v9)
%
% Copyright (C) 1993-2011, by Peter I. Corke
%
% This file is part of The Robotics Toolbox for MATLAB (RTB).
%
% RTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% RTB is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with RTB.  If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com
function s = char(robot)
%
% S = R.char() is a string representation of the robot parameters.

	s = '';
	for j=1:length(robot)
		r = robot(j);

		% informational line
		if r.mdh
			convention = 'modDH';
		else
			convention = 'stdDH';
		end
		s = sprintf('%s (%d axis, %s, %s)', r.name, r.n, config(r), convention);

		% comment and other info
		line = '';
		if ~isempty(r.manufacturer)
			line = strcat(line, sprintf(' %s;', r.manufacturer));
		end
		if ~isempty(r.comment)
			line = strcat(line, sprintf(' %s;', r.comment));
		end
		s = strvcat(s, line);

		% link parameters
		s = strvcat(s, '+---+-----------+-----------+-----------+-----------+');
		s = strvcat(s, '| j |     theta |         d |         a |     alpha |');
		s = strvcat(s, '+---+-----------+-----------+-----------+-----------+');
		s = strvcat(s, char(r.links, true));
		s = strvcat(s, '+---+-----------+-----------+-----------+-----------+');

		% gravity, base, tool

		s_grav = horzcat(strvcat('grav = ', ' ', ' '), num2str(r.gravity));
		s_grav = strvcat(s_grav, " ");
		s_base = horzcat(strvcat("  base = ",' ',' ', ' '),num2str(r.base));

		s_tool = horzcat(strvcat('   tool =  ',' ',' ', ' '), num2str(r.tool));

		line = horzcat(s_grav, s_base, s_tool);

		s = strvcat(s, ' ', line);
		if j ~= length(robot)
			s = strvcat(s, ' ');
		end
	end
endfunction

function v = config(r)
            v = '';
            for i=1:r.n
                v(i) = r.links(i).RP;
            end
        end


function [anat, inplanes] = GetAnatomyFromAnalyze(fileName)
% [anat, inplanes] = GetAnatomyFromAnalyze(fileName)
%
% Similar to getAnatomy but you pass in a 3d Analyze / nifti file that you
% already made. Requires SPM5
% ARW 061407

anat = [];

if ~exist(fileName, 'file');
    disp(fileName);
    disp('File does not exist')

    return;
end

    V=spm_vol(fileName);

    [anat,XYZ] = spm_read_vols(V);
      % Do a rot90 on each slice of the anat. That's just the way it is...
    for thisSlice=1:size(anat,3)
        anat2(:,:,thisSlice)=rot90(anat(:,:,thisSlice),1);
    end
    anat=anat2;

    % Use imatrix to recover slice spacing etc.
    [inTrans]=spm_imatrix(V.mat);

    sz = size(anat);
    mm=abs(inTrans(7:9));

    inplanes.FOV = mm(1)*sz(1);
    inplanes.fullSize = sz(1:2);
    inplanes.voxelSize = mm;
    inplanes.spacing = 0;
    % We already checked hdr.image.slquant==nList, not again -- Junjie
    inplanes.nSlices = sz(3);

    inplanes.examNum = V.descrip;
    inplanes.crop = [];
    inplanes.cropSize = [];


function [common, d1, d2] = comp_struct(s1,s2,prt,pse,tol,n1,n2)
% check two structures for differences - i.e. see if structure s1 == structure s2
% function [common, d1, d2] = comp_struct(s1,s2,prt,pse,tol)
%
% inputs  5 - 4 optional
% s1      structure one                              class structure
% s2      structure two                              class structure - optional
% prt     print test results (0 / 1 / 2 / 3)         class integer - optional
% pse     pause flag (0 / 1 / 2)                     class integer - optional
% tol     tol default tolerance (real numbers)       class integer - optional
%
% outputs 5 - 5 optional
% common  matching fields                            class struct - optional
% d1      non-matching fields for structure one      class struct - optional
% d2      non-matching fields for structure two      class struct - optional
%
% prt:
%	0 --> no print
%	1 --> print class erros
%	2 --> print all errors
% pse:
%	1 --> pause for class erros
%	2 --> pause for all errors
%
% example:	[same, er1, er2] = comp_struct(data1,data2,1,1,1e-6)
% michael arant - may 27, 2013
%
% updated - aug 22, 2015
%
% hint:
% passing just one structure causes the program to copy the structure
% and compare the two.  This is an easy way to list the structure
%
% note:  n1 and n2 are not required at launch - used for recursive purposes

%% default arguent check
if nargin < 2
	help comp_struct; error('I / O error');
end

if nargin < 3 || isempty(prt); prt = 0; end
if nargin < 4 || isempty(pse); pse = 0; elseif pse ~= 1 && prt == 0; pse = 0; end
if nargin < 5 || isempty(tol); tol = 1e-20; end
if pse > prt, pse = prt; end

if ~exist('n1','var'); n1 =  inputname(1); end
if ~exist('n2','var'); n2 = inputname(2); end

%% structure definition
d1 = s1; d2 = s2; common = s1;

%% begin analysis
flag = [0 0];
% test entire structure
if ~isequal(s1,s2)
	% differences noted - parse
	if isstruct(s1) && isstruct(s2)
		% both structures - once sub structures are tested, do not
		% modify the parrent
		flag(2) = 1;
		% both structures - get the field names for each structure
		fn1 = fieldnames(s1);
		fn2 = fieldnames(s2);
		% missing fields?  (common was a copy of s1 - so only s1 needs checking)
		temp = find(~ismember(fn1,fn2));
		for ii = 1:numel(temp)
			% drop unique fields
			common = rmfield(common,fn1{temp(ii)});
		end
		% get the common fields
		fn = fieldnames(common);
		% missing fields in set 1
		for ii = 1:numel(fn)
			% common field - recurse and test
			for jj = 1:min([numel(d1) numel(d2)])
				[common(jj).(fn{ii}) d1(jj).(fn{ii}) d2(jj).(fn{ii})] = ...
					comp_struct(s1(jj).(fn{ii}),s2(jj).(fn{ii}), ...
					prt,pse,tol, ...
					[n1 '(' num2str(jj) ').' fn{ii}], ...
					[n2 '(' num2str(jj) ').' fn{ii}]);
			end
		end
	% one or both not structures evaluate
	elseif isstruct(s1)
		% first variable is structure - second is not
		if prt
			fprintf('Error:	%s is a structure and %s is not a structure\n',n1,n2);
		end
		if pse
			uiwait(msgbox(sprintf('Error:  %s is a structure and %s is not a structure\n',n1,n2)))
		end
		% flag purge
		flag(1) = 1;

	elseif isstruct(s2)
		% second variable is structure - first is not
		if prt
			fprintf('Error:	%s is not a structure and %s is a structure\n',n1,n2);
		end
		if pse; pause; end
		% flag purge
		flag(1) = 1;
	else
		% the same?
		if ~isequal(s1,s2)
			flag(1) = 1;
			% not the same - difference?
			% class error
			if ~strcmp(class(s1),class(s2))
				% different classes
				if prt
					fprintf('Error:	%s is class %s and %s class %s\n',n1,class(s1),n2,class(s2));
				end
				if pse; pause; end
				flag(1) = 1;
			else
				% tolerance error?
				if min(size(s1) == size(s2)) && ...
						(isa(s1,'single') || isa(s1,'double'))
					% tolerance match?
					if ((max(s1)==0 || max(s2) ==0) && max(abs(s1-s2)) < tol) || numel(find(abs(s1-s2)./s1 < tol)) == numel(s1)
						flag(1) = 0;
					end
				else
					% print and pause?
					if prt > 1
						fprintf('%s is ',n1); disp(s1);
						fprintf('\b, %s is ',n2); disp(s2);
					end
					if pse > 1; pause; end
				end
			end
		end
	end
end

%% keep or delete
if flag(1) && ~flag(2)
	% denote error
	common = [];
elseif ~flag(2)
	% remove from error structures
	d1 = [];
	d2 = [];
end

%% purge empty fields
% test common
if isstruct(common)
	% fieldnames
	fn = fieldnames(common);
	for ii = 1:numel(fn)
		temp = 1;
		% test for empty field
		for jj = 1:numel(common)
			if ~isempty(common(jj).(fn{ii})); temp = 0; end
		end
		% purge if field was empty
		if temp
			common = rmfield(common,fn{ii});
		end
	end
end
% test d1
if isstruct(d1)
	% fieldnames
	fn = fieldnames(d1);
	for ii = 1:numel(fn)
		temp = 1;
		% test for empty field
		for jj = 1:numel(d1)
			if ~isempty(d1(jj).(fn{ii})); temp = 0; end
		end
		% purge if field was empty
		if temp
			d1 = rmfield(d1,fn{ii});
		end
	end
end
% test d2
if isstruct(d2)
	% fieldnames
	fn = fieldnames(d2);
	for ii = 1:numel(fn)
		temp = 1;
		% test for empty field
		for jj = 1:numel(d2)
			if ~isempty(d2(jj).(fn{ii})); temp = 0; end
		end
		% purge if field was empty
		if temp
			d2 = rmfield(d2,fn{ii});
		end
	end
end

%% test for null fields
if isstruct(common)
	% fieldnames
	if isempty(fieldnames(common)); common = []; end
end
if isstruct(d1)
	% fieldnames
	if isempty(fieldnames(d1)); d1 = []; end
end
if isstruct(d2)
	% fieldnames
	if isempty(fieldnames(d2)); d2 = []; end
end


function varargout = drawRay(ray, varargin)
%DRAWRAY Draw a ray on the current axis.
%
%   drawRay(RAY)
%   With RAY having the syntax: [x0 y0 dx dy], draws the ray starting from
%   point (x0 y0) and going to direction (dx dy), clipped with the current
%   window axis.
%
%   drawRay(RAY, PARAMS, VALUE)
%   Can specify param-pair values.
%
%   H = drawRay(...)
%   Returns handle on line object
%
%   See also
%     rays2d, drawLine, clipRay
%

% ------
% Author: David Legland
% E-mail: david.legland@inrae.fr
% Created: 2003-10-31
% Copyright 2003-2022 INRA - TPV URPOI - BIA IMASTE

% extract handle of axis to draw in
if isAxisHandle(ray)
    ax = ray;
    ray = varargin{1};
    varargin(1) = [];
else
    ax = gca;
end

% get bounding box limits
box = axis(ax);

% compute clipped shapes
[clipped, isInside] = clipRay(ray, box);

% allocate memory for handle
h = -ones(size(ray, 1), 1);

% draw visible rays
h(isInside) = drawEdge(ax, clipped(isInside, :), varargin{:});

% process output
if nargout > 0
    varargout = {h};
end


%LOADPGM	Load a PGM image
%
%	I = loadpgm(filename)
%
%	Returns a matrix containing the image loaded from the PGM format
%	file filename.  Handles ASCII (P2) and binary (P5) PGM file formats.
%
%	If the filename has no extension, and open fails, a '.pgm' will
%	be appended.  If the file cannot be opened it returns [].
%
%	Wildcards are allowed in file names.  If multiple files match
%	a 3D image is returned where the last dimension is the number
%	of images contained.
%
%	I = loadpgm
%
%	Presents a file selection GUI from which the user can pick a file.
%	Uses the same path as previous call.
%
%	Second return argument is the image creation time that comes
% from a TIMESPEC header comment (local convention).

% Copyright (C) 1993-2011, by Peter I. Corke
%
% This file is part of The Machine Vision Toolbox for Matlab (MVTB).
%
% MVTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% MVTB is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with MVTB.  If not, see <http://www.gnu.org/licenses/>.

function [I,t] = loadpgm(file)
	t = [];
	fid = fopen(filename, 'r');
	if fid < 0,
		fid = fopen([filename '.pgm'], 'r');
	end
	if fid < 0,
		I = [];
		error(['Couldn''t open file ' filename]);
	end

	white = [' ' 9 10 13];	% space, tab, lf, cr
	white = setstr(white);

	% read the PX header
	magic = fread(fid, 2, 'char');

	%  read cols and process any comment field before hand
	while 1
		c = fread(fid,1,'*char');
		if c == '#',
			comment = fgetl(fid);
			time = sscanf(comment, ' TIMESPEC %d %d');
			if length(time) == 2,
				t = time(1) + time(2)*1e-9;
			end
		elseif ~any(c == white)
			fseek(fid, -1, 'cof');	% unputc()
			break;
		end
	end
	cols = fscanf(fid, '%d', 1);

	%  read rows and process any comment field before hand
	while 1
		c = fread(fid,1,'*char');
		if c == '#',
			fgetl(fid);
		elseif ~any(c == white)
			fseek(fid, -1, 'cof');	% unputc()
			break;
		end
	end
	rows = fscanf(fid, '%d', 1);

	%  read maxval and process any comment field before hand
	while 1
		c = fread(fid,1,'*char');
		if c == '#',
			fgetl(fid);
		elseif ~any(c == white)
			fseek(fid, -1, 'cof');	% unputc()
			break;
		end
	end
	maxval = fscanf(fid, '%d', 1);

	% read the newline
	c = fread(fid,1,'*char');

	% Process the header info and read the image
	if magic(1) == 'P',
		if magic(2) == '2',
			fprintf('%s: ASCII PGM file (%d x %d)\n', filename, rows, cols)
			I = fscanf(fid, '%d', [cols rows])';
		elseif magic(2) == '5',
			fprintf('%s: binary PGM file (%d x %d)\n', filename, rows, cols)
			if maxval == 1,
				fmt = 'unint1';
			elseif maxval == 15,
				fmt = 'uint4';
			elseif maxval == 255,
				fmt = 'uint8';
			elseif maxval == 2^16-1,
				fmt = 'uint16';
			elseif maxval == 2^32-1,
				fmt = 'uint32';
			end
			I = fread(fid, [cols rows], fmt)';
		else
			disp('Not a PGM file');
		end
	end
	fclose(fid);


function s = freeSpace()

% FREESPACE Check for free space in Map.
%   FREESPACE() returns the number of free states in the map Map.
%
%   See also NEWRANGE, BLOCKRANGE, RANGES.

%   Copyright 2008-2009 Joan Sola @ LAAS-CNRS.
%   Copyright 2015-     Joan Sola @ IRI-CSIC-UPC

global Map

s = sum(~Map.used);

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB 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.
%
%   SLAMTB 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 SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


function dist = dist_euclidean(metric, x)
% DIST_EUCLIDEAN   is a mex file that returns the euclidean distance matrix
% for metric_euclidean.  If we end up here the mex file has not been
% compiled. In this case the function returns NaN and the distance is
% evaluated in metric_euclidean.

% Copyright (c) 2008 Jarno Vanhatalo

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

dist = NaN;


% NANMIN provides a replacement for MATLAB's nanmin that is almost
% compatible.

function [varargout] = nanmin(varargin)

% the standard MATLAB min function ignores NaNs from at least version 7.0.4 onwards
[varargout{1:nargout}] = min(varargin{:});


function[]=makefigs_seminfhaxby
%MAKEFIGS_SEMINFHAXBY  Makes a sample figure for SEMINFHAXBY.

figure
subplot(2,2,1),contourf(abs(peaks(200)),60),colormap(gca,'seminfhaxby'),%xlabel('seminf-haxby')
subplot(2,2,2),contourf(abs(peaks(200)),60),colormap(gca,'lansey'),%xlabel('lansey')
subplot(2,2,3),contourf(abs(peaks(200)),60),colormap(gca,'parula'),%xlabel('parula')
subplot(2,2,4),contourf(abs(peaks(200)),60),colormap(gca,'jet'),%xlabel('jet')
for i=1:4,subplot(2,2,i),axis equal,axis tight,caxis([0 8.2]),...
        noxlabels,noylabels,nocontours,ylim([-50 200])
end
h=packfig(2,2,'both');
str={'seminf-haxby','lansey','parula','jet'};
for i=1:4,axes(h(i))
    hc=colorbar('south'),
    %hc.AxisLocation='out';
    hc.Label.String=str{i};
end
set(gcf,'paperposition',[1 1 8 9.5 ])

%To print
if 0
    currentdir=pwd;
    cd([whichdir('jlab_license') '/figures'])
    print -dpng seminfhaxby
    crop seminfhaxby.png
    cd(currentdir)
end


function obj = addVariable(obj, label, nodes, varargin)
    % Adds NLP decision variables
    %
    % Parameters:
    % label: the label name (column) of the variable @type char
    % nodes: the node list of the variable @type rowvec
    % varargin: variable input arguments for property values non-empty
    % NlpVariables @copydoc NlpVariable::updateProp
    %
    % @see NlpVariable, removeVariable

    varnames = obj.OptVarTable.Properties.VariableNames;

    %     if ismember(label,varnames)
    %         warning('The NLP variable (%s) already exists.\n Overwriting the existing NLP variable.', label);
    %     end

    if ischar(nodes)
        switch nodes
            case 'first'
                node_list = 1;
            case 'last'
                node_list = obj.NumNode;
            case 'except-first'
                node_list = 2:obj.NumNode;
            case 'except-last'
                node_list = 1:obj.NumNode-1;
            case 'except-terminal'
                node_list = 2:obj.NumNode-1;
            case 'all'
                node_list = 1:obj.NumNode;
            case 'cardinal'
                node_list = 1:2:obj.NumNode;
            case 'interior'
                node_list = 2:2:obj.NumNode-1;
            otherwise
                error('Unknown node type.');
        end
    else
        if ~isnumeric(nodes)
            error(['The node must be specified as a list of node indices or following supported characters:\n',...
                '%s'],implode({'first','last','all','except-first','except-last','except-terminal', 'cardinal', 'interior'},','));
        else
            node_list = nodes;
        end
    end

    % create empty NlpVariable array
    var = repmat(NlpVariable(),obj.NumNode,1);
    for i=node_list
        var(i) = NlpVariable(varargin{:});
    end
    % add to the decision variable table
    obj.OptVarTable.(label) = var;

end


% testThetaChange
cnnConfig = config();
[Theta, meta] = cnnInitParams(cnnConfig);
newTheta = thetaChange(Theta, meta, 'vec2stack', cnnConfig);
oldTheta = thetaChange(newTheta, meta, 'stack2vec', cnnConfig);
newOldTheta = thetaChange(oldTheta, meta, 'vec2stack', cnnConfig);

if isequal(newTheta,newOldTheta);
    fprintf('The thetaConfig function works well, congratulations\n');
else
    fprintf('The thetaConfig function does not work well, please check!\n');
end

if isequal(Theta,oldTheta);
    fprintf('The thetaConfig function works well, congratulations\n');
else
    fprintf('The thetaConfig function does not work well, please check!\n');
end


function value = ch_is_control ( c )

%*****************************************************************************80
%
%% CH_IS_CONTROL is TRUE if C is a control character.
%
%  Discussion:
%
%    A "control character" has ASCII code <= 31 or 127 <= ASCII code.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    26 September 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character C, the character to be tested.
%
%    Output, logical VALUE, TRUE if C is a control character, and
%    FALSE otherwise.
%
  if ( c <= 31 || 127 <= c )
    value = 1;
  else
    value = 0;
  end

  return
end


function [net, stats] = cnn_train_binary(net, imdb, getBatch, varargin)
%CNN_TRAIN  An example implementation of SGD for training CNNs
%    CNN_TRAIN() is an example learner implementing stochastic
%    gradient descent with momentum to train a CNN. It can be used
%    with different datasets and tasks by providing a suitable
%    getBatch function.
%
%    The function automatically restarts after each training epoch by
%    checkpointing.
%
%    The function supports training on CPU or on one or more GPUs
%    (specify the list of GPU IDs in the `gpus` option).

% Copyright (C) 2014-16 Andrea Vedaldi.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).

opts.expDir = fullfile('data','exp') ;
opts.continue = true ;
opts.batchSize = 256 ;
opts.numSubBatches = 1 ;
opts.train = [] ;
opts.val = [] ;
opts.gpus = [] ;
opts.prefetch = false ;
opts.numEpochs = 300 ;
opts.learningRate = 0.001 ;
opts.weightDecay = 0.0005 ;
opts.momentum = 0.9 ;
opts.randomSeed = 0 ;
opts.memoryMapFile = fullfile(tempdir, 'matconvnet.bin') ;
opts.profile = false ;

opts.conserveMemory = true ;
opts.backPropDepth = +inf ;
opts.sync = false ;
opts.cudnn = true ;
opts.errorFunction = 'multiclass' ;
opts.errorLabels = {} ;
opts.plotDiagnostics = false ;
opts.plotStatistics = true;
opts = vl_argparse(opts, varargin) ;
opts.numGpus = numel(opts.gpus);
opts.bwn = true;
opts.xnor = false;

if ~exist(opts.expDir, 'dir'), mkdir(opts.expDir) ; end
if isempty(opts.train), opts.train = find(imdb.images.set==1) ; end
if isempty(opts.val), opts.val = find(imdb.images.set==2) ; end
if isnan(opts.train), opts.train = [] ; end
if isnan(opts.val), opts.val = [] ; end

% -------------------------------------------------------------------------
%                                                            Initialization
% -------------------------------------------------------------------------

net = vl_simplenn_tidy(net); % fill in some eventually missing values
net.layers{end-1}.precious = 1; % do not remove predictions, used for error
vl_simplenn_display(net, 'batchSize', opts.batchSize) ;

evaluateMode = isempty(opts.train) ;
if ~evaluateMode
  for i=1:numel(net.layers)
    if isfield(net.layers{i}, 'weights')
      J = numel(net.layers{i}.weights) ;
      if ~isfield(net.layers{i}, 'learningRate')
        net.layers{i}.learningRate = ones(1, J, 'single') ;
      end
      if ~isfield(net.layers{i}, 'weightDecay')
        net.layers{i}.weightDecay = ones(1, J, 'single') ;
      end
    end
  end
end

% setup error calculation function
hasError = true ;
if isstr(opts.errorFunction)
  switch opts.errorFunction
    case 'none'
      opts.errorFunction = @error_none ;
      hasError = false ;
    case 'multiclass'
      opts.errorFunction = @error_multiclass ;
      if isempty(opts.errorLabels), opts.errorLabels = {'top1err', 'top5err'} ; end
    case 'binary'
      opts.errorFunction = @error_binary ;
      if isempty(opts.errorLabels), opts.errorLabels = {'binerr'} ; end
    otherwise
      error('Unknown error function ''%s''.', opts.errorFunction) ;
  end
end

state.getBatch = getBatch ;
stats = [] ;

% -------------------------------------------------------------------------
%                                                        Train and validate
% -------------------------------------------------------------------------

modelPath = @(ep) fullfile(opts.expDir, sprintf('net-epoch-%d.mat', ep));
modelFigPath = fullfile(opts.expDir, 'net-train.pdf') ;

start = opts.continue * findLastCheckpoint(opts.expDir) ;
if start >= 1
  fprintf('%s: resuming by loading epoch %d\n', mfilename, start) ;
  [net, stats] = loadState(modelPath(start)) ;
end

for epoch=start+1:opts.numEpochs

  % Set the random seed based on the epoch and opts.randomSeed.
  % This is important for reproducibility, including when training
  % is restarted from a checkpoint.

  rng(epoch + opts.randomSeed) ;
  prepareGPUs(opts, epoch == start+1) ;

  % Train for one epoch.

  state.epoch = epoch ;
  state.learningRate = opts.learningRate(min(epoch, numel(opts.learningRate))) ;
  state.train = opts.train(randperm(numel(opts.train))) ; % shuffle
  state.val = opts.val(randperm(numel(opts.val))) ;
  state.imdb = imdb ;

  if numel(opts.gpus) <= 1
    [net,stats.train(epoch),prof] = process_epoch(net, state, opts, 'train') ;
    [~,stats.val(epoch)] = process_epoch(net, state, opts, 'val') ;
    if opts.profile
      profview(0,prof) ;
      keyboard ;
    end
  else
    spmd(numGpus)
      [net_, stats_.train, prof_] = process_epoch(net, state, opts, 'train') ;
      [~, stats_.val] = process_epoch(net_, state, opts, 'val') ;
      if labindex == 1, savedNet_ = net_ ; end
    end
    net = savedNet_{1} ;
    stats__ = accumulateStats(stats_) ;
    stats.train(epoch) = stats__.train ;
    stats.val(epoch) = stats__.val ;
    if opts.profile
      mpiprofile('viewer', [prof_{:,1}]) ;
      keyboard ;
    end
    clear net_ stats_ stats__ savedNet_ ;
  end

  % save
  if ~evaluateMode
    saveState(modelPath(epoch), net, stats) ;
  end

  if opts.plotStatistics
    switchFigure(1) ; clf ;
    plots = setdiff(...
      cat(2,...
      fieldnames(stats.train)', ...
      fieldnames(stats.val)'), {'num', 'time'}) ;
    for p = plots
      p = char(p) ;
      values = zeros(0, epoch) ;
      leg = {} ;
      for f = {'train', 'val'}
        f = char(f) ;
        if isfield(stats.(f), p)
          tmp = [stats.(f).(p)] ;
          values(end+1,:) = tmp(1,:)' ;
          leg{end+1} = f ;
        end
      end
      subplot(1,numel(plots),find(strcmp(p,plots))) ;
      plot(1:epoch, values','o-') ;
      xlabel('epoch') ;
      title(p) ;
      legend(leg{:}) ;
      grid on ;
    end
    drawnow ;
    print(1, modelFigPath, '-dpdf') ;
  end
end

% -------------------------------------------------------------------------
function err = error_multiclass(opts, labels, res)
% -------------------------------------------------------------------------
predictions = gather(res(end-1).x) ;
[~,predictions] = sort(predictions, 3, 'descend') ;

% be resilient to badly formatted labels
if numel(labels) == size(predictions, 4)
  labels = reshape(labels,1,1,1,[]) ;
end

% skip null labels
mass = single(labels(:,:,1,:) > 0) ;
if size(labels,3) == 2
  % if there is a second channel in labels, used it as weights
  mass = mass .* labels(:,:,2,:) ;
  labels(:,:,2,:) = [] ;
end

m = min(5, size(predictions,3)) ;

error = ~bsxfun(@eq, predictions, labels) ;
err(1,1) = sum(sum(sum(mass .* error(:,:,1,:)))) ;
err(2,1) = sum(sum(sum(mass .* min(error(:,:,1:m,:),[],3)))) ;

% -------------------------------------------------------------------------
function err = error_binary(opts, labels, res)
% -------------------------------------------------------------------------
predictions = gather(res(end-1).x) ;
error = bsxfun(@times, predictions, labels) < 0 ;
err = sum(error(:)) ;

% -------------------------------------------------------------------------
function err = error_none(opts, labels, res)
% -------------------------------------------------------------------------
err = zeros(0,1) ;

% -------------------------------------------------------------------------
function  [net_cpu,stats,prof] = process_epoch(net, state, opts, mode)
% -------------------------------------------------------------------------

% initialize empty momentum
if strcmp(mode,'train')
  state.momentum = {} ;
  for i = 1:numel(net.layers)
    if isfield(net.layers{i}, 'weights')
      for j = 1:numel(net.layers{i}.weights)
        state.layers{i}.momentum{j} = 0 ;
      end
    end
  end
end

% move CNN  to GPU as needed
numGpus = numel(opts.gpus) ;
if numGpus >= 1
  net = vl_simplenn_move(net, 'gpu') ;
end
if numGpus > 1
  mmap = map_gradients(opts.memoryMapFile, net, numGpus) ;
else
  mmap = [] ;
end

% profile
if opts.profile
  if numGpus <= 1
    profile clear ;
    profile on ;
  else
    mpiprofile reset ;
    mpiprofile on ;
  end
end

subset = state.(mode) ;
num = 0 ;
stats.num = 0 ; % return something even if subset = []
stats.time = 0 ;
adjustTime = 0 ;
res = [] ;
error = [] ;

start = tic ;
for t=1:opts.batchSize:numel(subset)
  fprintf('%s: epoch %02d: %3d/%3d:', mode, state.epoch, ...
          fix((t-1)/opts.batchSize)+1, ceil(numel(subset)/opts.batchSize)) ;
  batchSize = min(opts.batchSize, numel(subset) - t + 1) ;

  for s=1:opts.numSubBatches
    % get this image batch and prefetch the next
    batchStart = t + (labindex-1) + (s-1) * numlabs ;
    batchEnd = min(t+opts.batchSize-1, numel(subset)) ;
    batch = subset(batchStart : opts.numSubBatches * numlabs : batchEnd) ;
    num = num + numel(batch) ;
    if numel(batch) == 0, continue ; end

    [im, labels] = state.getBatch(state.imdb, batch) ;

    if opts.prefetch
      if s == opts.numSubBatches
        batchStart = t + (labindex-1) + opts.batchSize ;
        batchEnd = min(t+2*opts.batchSize-1, numel(subset)) ;
      else
        batchStart = batchStart + numlabs ;
      end
      nextBatch = subset(batchStart : opts.numSubBatches * numlabs : batchEnd) ;
      state.getBatch(state.imdb, nextBatch) ;
    end

    if numGpus >= 1
      im = gpuArray(im) ;
    end

    if strcmp(mode, 'train')
      dzdy = 1 ;
      evalMode = 'normal' ;
    else
      dzdy = [] ;
      evalMode = 'test' ;
    end
    net.layers{end}.class = labels ;
    if opts.bwn == true
        net = bwn_net(net);
    end
    if opts.xnor == true
        net = bwn_net(net);
    end
    % forward and backward
    res = vl_simplenn(net, im, dzdy, res, ...
                      'accumulate', s ~= 1, ...
                      'mode', evalMode, ...
                      'conserveMemory', opts.conserveMemory, ...
                      'backPropDepth', opts.backPropDepth, ...
                      'sync', opts.sync, ...
                      'cudnn', opts.cudnn) ;
    % bwn net or xnor net
    if opts.bwn == true
        [bnet,net] = bwn_net_back(net);
    end
    if opts.xnor == true
        [bnet,net] = bwn_net_back(net);
    end
    % accumulate errors

    error = sum([error, [...
      sum(double(gather(res(end).x))) ;
      reshape(opts.errorFunction(opts, labels, res),[],1) ; ]],2) ;
  end

  % accumulate gradient
  if strcmp(mode, 'train')
    if ~isempty(mmap)
      write_gradients(mmap, net) ;
      labBarrier() ;
    end
    [state, net] = accumulate_gradients(state, net, res, opts, batchSize, mmap) ;
  end

  % get statistics
  time = toc(start) + adjustTime ;
  batchTime = time - stats.time ;
  stats = extractStats(net, opts, error / num) ;
  stats.num = num ;
  stats.time = time ;
  currentSpeed = batchSize / batchTime ;
  averageSpeed = (t + batchSize - 1) / time ;
  if t == opts.batchSize + 1
    % compensate for the first iteration, which is an outlier
    adjustTime = 2*batchTime - time ;
    stats.time = time + adjustTime ;
  end

  fprintf(' %.1f (%.1f) Hz', averageSpeed, currentSpeed) ;
  for f = setdiff(fieldnames(stats)', {'num', 'time'})
    f = char(f) ;
    fprintf(' %s:', f) ;
    fprintf(' %.3f', stats.(f)) ;
  end
  fprintf('\n') ;

  % collect diagnostic statistics
  if strcmp(mode, 'train') && opts.plotDiagnostics
    switchfigure(2) ; clf ;
    diagn = [res.stats] ;
    diagnvar = horzcat(diagn.variation) ;
    barh(diagnvar) ;
    set(gca,'TickLabelInterpreter', 'none', ...
      'YTick', 1:numel(diagnvar), ...
      'YTickLabel',horzcat(diagn.label), ...
      'YDir', 'reverse', ...
      'XScale', 'log', ...
      'XLim', [1e-5 1]) ;
    drawnow ;
  end
end

if ~isempty(mmap)
  unmap_gradients(mmap) ;
end

if opts.profile
  if numGpus <= 1
    prof = profile('info') ;
    profile off ;
  else
    prof = mpiprofile('info');
    mpiprofile off ;
  end
else
  prof = [] ;
end

net_cpu = vl_simplenn_move(net, 'cpu') ;

% -------------------------------------------------------------------------
function [bnet,net] = bwn_net_back(net)
% -------------------------------------------------------------------------
n_layers = numel(net.layers);
bnet = net;
for ii = 1:n_layers
    if strcmp(net.layers{ii}.type,'conv')
        net.layers{ii}.weights{1} = net.layers{ii}.Bs{1};
        net.layers{ii}.weights{2} = net.layers{ii}.Bs{2};
    end
end

% -------------------------------------------------------------------------
function [net] = bwn_net(net)
% -------------------------------------------------------------------------
n_layers = numel(net.layers);
for ii = 1:n_layers
    if strcmp(net.layers{ii}.type,'conv')
        for jj = 1:numel(net.layers{ii}.alpha{1})
            temp_W = net.layers{ii}.weights{1};
            temp_b = net.layers{ii}.weights{2};
            temp_BW = ones(size(temp_W),'single');
            temp_Bb = ones(size(temp_b),'single');

            temp_BW(net.layers{ii}.weights{1} < 0) = -1;
            temp_Bb(net.layers{ii}.weights{2} < 0) = -1;

            net.layers{ii}.alpha{1,jj} = sum(temp_W(:))/numel(net.layers{ii}.weights{1});
            net.layers{ii}.alpha{2,jj} = sum(temp_b(:))/numel(net.layers{ii}.weights{1});

            net.layers{ii}.Bs{1} = temp_W;
            net.layers{ii}.Bs{2} = temp_b;

            net.layers{ii}.weights{1} = net.layers{ii}.alpha{1,jj}*temp_BW;
            net.layers{ii}.weights{2} = net.layers{ii}.alpha{2,jj}*temp_Bb;
        end
    end
end

% -------------------------------------------------------------------------
function [state, net] = accumulate_gradients(state, net, res, opts, batchSize, mmap)
% -------------------------------------------------------------------------
numGpus = numel(opts.gpus) ;
otherGpus = setdiff(1:numGpus, labindex) ;

for l=numel(net.layers):-1:1
  for j=1:numel(res(l).dzdw)

    % accumualte gradients from multiple labs (GPUs) if needed
    if numGpus > 1
      tag = sprintf('l%d_%d',l,j) ;
      for g = otherGpus
        tmp = gpuArray(mmap.Data(g).(tag)) ;
        res(l).dzdw{j} = res(l).dzdw{j} + tmp ;
      end
    end

    if j == 3 && strcmp(net.layers{l}.type, 'bnorm')
      % special case for learning bnorm moments
      thisLR = net.layers{l}.learningRate(j) ;
      net.layers{l}.weights{j} = ...
        (1 - thisLR) * net.layers{l}.weights{j} + ...
        (thisLR/batchSize) * res(l).dzdw{j} ;
    else
      % standard gradient training
      thisDecay = opts.weightDecay * net.layers{l}.weightDecay(j) ;
      thisLR = state.learningRate * net.layers{l}.learningRate(j) ;
      state.layers{l}.momentum{j} = opts.momentum * state.layers{l}.momentum{j} ...
        - thisDecay * net.layers{l}.weights{j} ...
        - (1 / batchSize) * res(l).dzdw{j} ;
      net.layers{l}.weights{j} = net.layers{l}.weights{j} + ...
        thisLR * state.layers{l}.momentum{j} ;
    end

    % if requested, collect some useful stats for debugging
    if opts.plotDiagnostics
      variation = [] ;
      label = '' ;
      switch net.layers{l}.type
        case {'conv','convt'}
          variation = thisLR * mean(abs(state.layers{l}.momentum{j}(:))) ;
          if j == 1 % fiters
            base = mean(abs(net.layers{l}.weights{j}(:))) ;
            label = 'filters' ;
          else % biases
            base = mean(abs(res(l+1).x(:))) ;
            label = 'biases' ;
          end
          variation = variation / base ;
          label = sprintf('%s_%s', net.layers{l}.name, label) ;
      end
      res(l).stats.variation(j) = variation ;
      res(l).stats.label{j} = label ;
    end
  end
end

% -------------------------------------------------------------------------
function mmap = map_gradients(fname, net, numGpus)
% -------------------------------------------------------------------------
format = {} ;
for i=1:numel(net.layers)
  for j=1:numel(net.layers(i).params)
    par = net.layers(i).params{j} ;
    format(end+1,1:3) = {'single', size(par), sprintf('l%d_%d',i,j)} ;
  end
end
format(end+1,1:3) = {'double', [3 1], 'errors'} ;
if ~exist(fname) && (labindex == 1)
  f = fopen(fname,'wb') ;
  for g=1:numGpus
    for i=1:size(format,1)
      fwrite(f,zeros(format{i,2},format{i,1}),format{i,1}) ;
    end
  end
  fclose(f) ;
end
labBarrier() ;
mmap = memmapfile(fname, ...
                  'Format', format, ...
                  'Repeat', numGpus, ...
                  'Writable', true) ;

% -------------------------------------------------------------------------
function write_gradients(mmap, net, res)
% -------------------------------------------------------------------------
for i=1:numel(net.layers)
  for j=1:numel(res(i).dzdw)
    mmap.Data(labindex).(sprintf('l%d_%d',i,j)) = gather(res(i).dzdw{j}) ;
  end
end

% -------------------------------------------------------------------------
function unmap_gradients(mmap)
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
function stats = accumulateStats(stats_)
% -------------------------------------------------------------------------

for s = {'train', 'val'}
  s = char(s) ;
  total = 0 ;

  % initialize stats stucture with same fields and same order as
  % stats_{1}
  stats__ = stats_{1} ;
  names = fieldnames(stats__.(s))' ;
  values = zeros(1, numel(names)) ;
  fields = cat(1, names, num2cell(values)) ;
  stats.(s) = struct(fields{:}) ;

  for g = 1:numel(stats_)
    stats__ = stats_{g} ;
    num__ = stats__.(s).num ;
    total = total + num__ ;

    for f = setdiff(fieldnames(stats__.(s))', 'num')
      f = char(f) ;
      stats.(s).(f) = stats.(s).(f) + stats__.(s).(f) * num__ ;

      if g == numel(stats_)
        stats.(s).(f) = stats.(s).(f) / total ;
      end
    end
  end
  stats.(s).num = total ;
end

% -------------------------------------------------------------------------
function stats = extractStats(net, opts, errors)
% -------------------------------------------------------------------------
stats.objective = errors(1) ;
for i = 1:numel(opts.errorLabels)
  stats.(opts.errorLabels{i}) = errors(i+1) ;
end

% -------------------------------------------------------------------------
function saveState(fileName, net, stats)
% -------------------------------------------------------------------------
save(fileName, 'net', 'stats') ;

% -------------------------------------------------------------------------
function [net, stats] = loadState(fileName)
% -------------------------------------------------------------------------
load(fileName, 'net', 'stats') ;
net = vl_simplenn_tidy(net) ;

% -------------------------------------------------------------------------
function epoch = findLastCheckpoint(modelDir)
% -------------------------------------------------------------------------
list = dir(fullfile(modelDir, 'net-epoch-*.mat')) ;
tokens = regexp({list.name}, 'net-epoch-([\d]+).mat', 'tokens') ;
epoch = cellfun(@(x) sscanf(x{1}{1}, '%d'), tokens) ;
epoch = max([epoch 0]) ;

% -------------------------------------------------------------------------
function switchFigure(n)
% -------------------------------------------------------------------------
if get(0,'CurrentFigure') ~= n
  try
    set(0,'CurrentFigure',n) ;
  catch
    figure(n) ;
  end
end

% -------------------------------------------------------------------------
function prepareGPUs(opts, cold)
% -------------------------------------------------------------------------
numGpus = numel(opts.gpus) ;
if numGpus > 1
  % check parallel pool integrity as it could have timed out
  pool = gcp('nocreate') ;
  if ~isempty(pool) && pool.NumWorkers ~= numGpus
    delete(pool) ;
  end
  pool = gcp('nocreate') ;
  if isempty(pool)
    parpool('local', numGpus) ;
    cold = true ;
  end
  if exist(opts.memoryMapFile)
    delete(opts.memoryMapFile) ;
  end
end
if numGpus >= 1 && cold
  fprintf('%s: resetting GPU\n', mfilename)
  if numGpus == 1
    gpuDevice(opts.gpus)
  else
    spmd, gpuDevice(opts.gpus(labindex)), end
  end
end


function header = egi_read_header(fid)
% EGI_READ_HEADER: Read header from an open EGI .raw file.
%
% USAGE:  header = egi_read_header(fid);

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

% ===== READ RAW HEADER =====
header.versionNumber          = fread(fid, 1, 'integer*4');
header.recordingTime.Year     = fread(fid, 1, 'integer*2');
header.recordingTime.Month    = fread(fid, 1, 'integer*2');
header.recordingTime.Day      = fread(fid, 1, 'integer*2');
header.recordingTime.Hour     = fread(fid, 1, 'integer*2');
header.recordingTime.Minute   = fread(fid, 1, 'integer*2');
header.recordingTime.Second   = fread(fid, 1, 'integer*2');
header.recordingTime.Millisec = fread(fid, 1, 'integer*4');
header.samplingRate           = fread(fid, 1, 'integer*2');
header.numChans               = fread(fid, 1, 'integer*2');
header.boardGain              = fread(fid, 1, 'integer*2');
header.numConvBits            = fread(fid, 1, 'integer*2');
header.ampRange               = fread(fid, 1, 'integer*2');

% ===== DATA FORMAT =====
switch header.versionNumber
    case {2,3}
        header.byteformat = 'integer*2';
        header.bytesize   = 2;
    case {4,5}
        header.byteformat = 'real*4';
        header.bytesize   = 4;
    case {6,7}
        header.byteformat = 'real*8';
        header.bytesize   = 8;
    otherwise
        fclose(fid);
        error('Brainstorm:InvalidRawFile', 'Error (Version ID): %d', header.versionNumber);
end
isReadCell = ismember(header.versionNumber, [3 5 7]);
if isReadCell
    fclose(fid);
    error('Support only for continuous EGI RAW files.');
end

% % ===== READ CELL NAMES =====
% if isReadCell
%     header.numCell = fread(fid, 1, 'int16');
%     % Read cells info
%     header.cellCodes = cell(1,header.numCell);
%     for i = 1 : header.numCell
%         codeSize = fread(fid, 1, 'int8');
%         header.cellCodes{i} = fread(fid, [1, codeSize], '*char');
%     end
%     metaData.nSeg = fread(fid, 1, 'int16');
% else
%     header.numCell = 0;
%     header.nSeg = 0;
%     header.cellCodes = {};
% end

% ===== READ EVENTS =====
header.numSamples = fread(fid, 1, 'integer*4');
header.numEvents  = fread(fid, 1, 'integer*2');
% File contains event info.
if (header.numEvents ~= 0)
    header.eventCodes = cell(header.numEvents, 1);
    for i = 1:header.numEvents
        header.eventCodes{i} = fread(fid, [1 4], '*char');
    end
else  % File does not contain event info.
    % warning('Brainstorm:IncompleteRawFile', 'RAW File does not contain event information');
    header.evtCode = [];
end

% Save beginning of the data block
header.datapos = ftell(fid);


function [funs, ends] = constructor(op, dom, data, pref)
%CONSTRUCTOR   CHEBFUN constructor.
%   FUNS = CONSTRUCTOR(OP, DOM) constructs the piecewise components (known as
%   "FUNS") used by a CHEBFUN object to represent the function OP on the
%   interval DOM. OP must be a function_handle, string, numerical vector, or a
%   cell array containing a combination of these first three data types. In the
%   later case, the number of elements in the array must be one less than the
%   length of the DOM vector.
%
%   It is not expected that CHEBFUN.CONSTRUCTOR() be called directly,
%
%   If OP is a function_handle or a string, it should be vectorised in that it
%   accepts a column vector of length N and return a matrix of size N x M. If M
%   ~= 1, we say the resulting CHEBFUN is "array-valued".
%
%   CONSTRUCTOR(OP, DOM, DATA, PREF), where DATA is a MATLAB structure and PREF
%   is a CHEBFUNPREF object, allows construction data and alternative
%   construction preferences to be passed to the constructor.  See CHEBFUNPREF
%   for more details on preferences.
%
%   In particular, if PREF.SPLITTING = TRUE and OP is a function_handle or a
%   string, then the constructor adaptively introduces additional breakpoints
%   into the domain so as to better represent the function. These are returned
%   as the second output argument in [FUNS, END] = CONSTRUCTOR(OP, DOM).
%
%   The DATA structure input contains information which needs to be passed to
%   the lower layers about parameters which may affect the construction process.
%   Presently, the only fields CONSTRUCTOR expects DATA to have on input are
%   DATA.EXPONENTS and DATA.SINGTYPE, which convey information about endpoint
%   singularities. These fields are populated by CHEBFUN.PARSEINPUTS as need be.
%   Before calling the FUN constructor, DATA will be augmented to include
%   information about the construction domain as well as the horizontal and
%   vertical scales involved in the construction procedure.
%
% See also CHEBFUN, CHEBFUNPREF.

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

% Initial setup:
numIntervals = numel(dom) - 1;

% Initialise hscale and vscale:
data.hscale = norm(dom, inf);
if ( isinf(data.hscale) )
    data.hscale = 1;
end
if ( isempty(data.vscale) )
    data.vscale = 0;
end

% Sanity check:
if ( iscell(op) && (numel(op) ~= numIntervals) )
    error('CHEBFUN:CHEBFUN:constructor:cellInput', ...
        ['Number of cell elements in OP must match the number of ', ...
         'intervals in DOMAIN.'])
end

% Construct the FUNs.
if ( pref.splitting )
    [funs, ends] = constructorSplit(op, dom, data, pref);
else
    [funs, ends] = constructorNoSplit(op, dom, data, pref);
end

end

%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%  SPLITTING OFF %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [funs, ends] = ...
    constructorNoSplit(op, domain, data, pref)
% In 'OFF' mode, seek only one piece with length < maxLength.

% Initial setup:
numIntervals = numel(domain) - 1;
ends = domain;

% Initialise the FUN array:
funs = cell(1, numIntervals);

% We only want to throw the warning 'CHEBFUN:constructor:notResolved'once:
warningThrown = false;

singDetect = pref.blowup;
exps = data.exponents;
singTypes = data.singType;

% Loop over the intervals:
for k = 1:numIntervals
    endsk = ends(k:k+1);
    % Unwrap if OP is a cell:
    if ( iscell(op) )
        opk = op{k};
    else
        opk = op;
    end

    if ( singDetect )
        % Extract the exponents for this interval:
        if ( ~isempty(exps) )
            data.exponents = exps(2*k-1:2*k);
        end
        % Replace the information for the singularity type in the preference:
        if ( ~isempty(singTypes) && ~ischar(singTypes) )
            data.singType = singTypes(2*k-1:2*k);
        end
    end

    % Call GETFUN() (which calls FUN.CONSTRUCTOR()):
    [funs{k}, ishappy, data.vscale] = getFun(opk, endsk, data, pref);

    % Warn if unhappy (as we're unable to split the domain to improve):
    if ( ~ishappy && ~warningThrown )
        if ( strcmpi(func2str(pref.tech), 'trigtech') )
            str = 'a non-trig representation';
        else
            str = '''splitting on''';
        end

        warning('CHEBFUN:CHEBFUN:constructor:notResolved', ...
            ['Function not resolved using %d pts.', ...
            ' Have you tried ' str, '?'], pref.techPrefs.maxLength);
        warningThrown = true;
    end
end

end

%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%  SPLITTING ON %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [funs, ends] = constructorSplit(op, dom, data, pref)
% In 'ON' mode, seek only many pieces with total length < maxlength.

% Initial setup:
numIntervals = numel(dom) - 1;
ends = dom;

% Set the maximum length (i.e., number of sample points for CHEBTECH):
pref.techPrefs.maxLength = pref.splitPrefs.splitLength;

% We extrapolate when splitting so that we can construct functions like
% chebfun(@sign,[-1 1]), which otherwise would not be happy at x = 0.
pref.techPrefs.extrapolate = true;

% Initialise the FUN array:
funs = cell(1, numIntervals);
% Initialise happiness:
ishappy = ones(1, numel(ends) - 1);

singDetect = pref.blowup;
exps = data.exponents;
singTypes = data.singType;

% Try to get one smooth piece for the entire domain before splitting:
for k = 1:numIntervals
    % Unwrap if OP is a cell:
    if ( iscell(op) )
        opk = op{k};
    else
        opk = op;
    end

    if ( singDetect )
        % Extract the singularity information for this interval:
        data = getSingInfo(exps, singTypes, 2*k-1:2*k, data);
    end

    [funs{k}, ishappy(k), data.vscale] = ...
        getFun(opk, ends(k:k+1), data, pref);

    % For the case where vscale is Inf due to blowup in the interior of the
    % domain:
    if ( isinf(data.vscale) )
        % An infinite vscale doesn't help us at all, but ruin the consequential
        % constructions at the lower levels:
        data.vscale = 0;
    end

end
sad = ~ishappy;

% MAIN LOOP. If the above didn't work, enter main loop and start splitting.
% (Note that at least one new breakpoint will be introduced).
while ( any(sad) )
    % If a FUN is sad in a subinterval, split this subinterval.

    % Choose a subinterval to improve:

%     % Old choice = the first sad interval:
%     k = find(sad, 1, 'first');

    % New choice = the largest sad interval:
    diffEnds = diff(ends);
    diffEnds(~sad) = 0;
    [ignored, k] = max(diffEnds);

    % Ends of this subinterval:
    a = ends(k);
    b = ends(k+1);

    % Unwrap if OP is a cell:
    if ( iscell(op) )
        opk = op{k};
    else
        opk = op;
    end

    % Look for an edge:
    edge = fun.detectEdge(funs{k}, opk, data.hscale, data.vscale, pref);

    if ( singDetect )
        % Update singularity info:
        if ( ~isempty(exps) )
            exps = [exps(1:2*k-1), zeros(1,2), exps(2*k:end)];
        end
        if ( ~isempty(singTypes) )
            singTypes = [singTypes(1:2*k-1), singTypes(2*k-1), ...
                singTypes(2*k) singTypes(2*k:end)];
        end
    end

    if ( singDetect )
        % Extract the singularity information for this interval:
        data = getSingInfo(exps, singTypes, 2*k-1:2*k, data);
    end

    % Try to obtain happy child FUN objects on each new subinterval:
    [childLeft, happyLeft, data.vscale] = getFun(opk, [a, edge], data, pref);

    if ( singDetect )
        % Extract the singularity information for this interval:
        data = getSingInfo(exps, singTypes, 2*k+1:2*k+2, data);
    end

    [childRight, happyRight, data.vscale] = getFun(opk, [edge, b], data, pref);

    % Check for happiness/sadness:
    sad = [sad(1:k-1), ~happyLeft, ~happyRight, sad(k+1:end)];

    % Insert new pieces in to existing funs:
    funs = [funs(1:k-1), {childLeft, childRight}, funs(k+1:end)];
    ends = [ends(1:k), edge, ends(k+1:end)];

    % If a cell was given, we must store pieces on new intervals:
    if ( iscell(op) )
        op = [op(1:k), {opk}, op(k+1:end)];
    end

    % Fail if too many points are required:
    len = sum(cellfun(@length, funs));
    if ( len > pref.splitPrefs.splitMaxLength )
        warning('CHEBFUN:CHEBFUN:constructor:funNotResolved', ...
            'Function not resolved using %d pts.', ...
            sum(cellfun(@length, funs)));
        return
    end

end

end

%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% GETFUN %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [g, ishappy, vscale] = getFun(op, dom, data, pref)
%GETFUN   Call the FUN constructor.

% If the interval is very small then skip adaptation and treat OP as a constant:
if ( diff(dom) < 4*1e-14*data.hscale && ~isnumeric(op) )
    op = op(mean(dom));
end

% Bolt domain information onto the data structure.
data.domain = dom;

% Call the FUN constructor:
g = fun.constructor(op, data, pref);

% See if the construction was happy:
ishappy = get(g, 'ishappy');

% Update the vertical scale:
if ( ishappy )
    vscale = max([data.vscale, get(g, 'vscale')]);
else
    vscale = data.vscale;
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% GETSINGINFO %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function data = getSingInfo(exps, singTypes, kk, data)
% Place information about the singularity type in the data structure.
if ( ~isempty(exps) )
    data.exponents = exps(kk);
end
if ( ~isempty(singTypes) )
    data.singType = singTypes(kk);
end
end


%kkprstats 'Print statistics to Cantata variables'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros kprstats.pane file
%
% Parameters:
% InputFile: i 'Input ', required: 'First Input data object'
% String: wmin_var ' ', default: 'width_min': 'Name of minimum width variable'
% String: wmax_var ' ', default: 'width_max': 'Name of maximum width variable'
% String: hmin_var ' ', default: 'height_min': 'Name of minimum height variable'
% String: hmaxvar ' ', default: 'height_max': 'Name of maximum height variable'
% String: dmin_var ' ', default: 'depth_min': 'Name of minimum depth variable'
% String: dmax_var ' ', default: 'depth_max': 'Name of maximum depth variable'
% String: tmin_var ' ', default: 'time_min': 'Name of minimum time variable'
% String: tmax_var ' ', default: 'time_max': 'Name of maximum time variable'
% String: emin_var ' ', default: 'elem_min': 'Name of minimum element variable'
% String: emax_var ' ', default: 'elem_max': 'Name of maximum element variable'
% String: minval_var ' ', default: 'val_min': 'Name of minimum value variable'
% String: maxval_var ' ', default: 'val_max': 'Name of maximum value variable'
% String: mean_var ' ', default: 'mean': 'Name for mean variable'
% String: wsize_var ' ', default: 'width_size': 'Name of total width variable (KSTATS_WSIZE)'
% String: var_var ' ', default: 'variance': 'Name for variance variable'
% String: hsize_var ' ', default: 'height_size': 'Name of total height variable (KSTATS_HSIZE)'
% String: sd_var ' ', default: 'std_dev': 'Name for standard deviation variable'
% String: dsize_var ' ', default: 'depth_size': 'Name of total depth variable'
% String: rms_var ' ', default: 'rms': 'Name for rms variable'
% String: tsize_var ' ', default: 'time_size': 'Name of total time variable'
% String: psum_var ' ', default: 'psum': 'Name of positive sum variable'
% String: esize_var ' ', default: 'elem_size': 'Name of total element variable'
% String: nsum_var ' ', default: 'nsum': 'Name for negative sum variable'
% String: sum_var ' ', default: 'sum': 'Name for sum variable'
% String: skew_var ' ', default: 'skew': 'Name for skew variable'
% String: kur_var ' ', default: 'kurtosis': 'Name for kurtosis variable'
% String: pts_var ' ', default: 'total_pts': 'Name for total points variable'
% String: ppts_var ' ', default: 'pos_pts': 'Name for positive points variable'
% String: npts_var ' ', default: 'neg_pts': 'Name for negative points variable'
% String: zpts_var ' ', default: 'zero_pts': 'Name for zero-valued points variable'
%
% Example: kkprstats(i, {'i','';'wmin_var','width_min';'wmax_var','width_max';'hmin_var','height_min';'hmaxvar','height_max';'dmin_var','depth_min';'dmax_var','depth_max';'tmin_var','time_min';'tmax_var','time_max';'emin_var','elem_min';'emax_var','elem_max';'minval_var','val_min';'maxval_var','val_max';'mean_var','mean';'wsize_var','width_size';'var_var','variance';'hsize_var','height_size';'sd_var','std_dev';'dsize_var','depth_size';'rms_var','rms';'tsize_var','time_size';'psum_var','psum';'esize_var','elem_size';'nsum_var','nsum';'sum_var','sum';'skew_var','skew';'kur_var','kurtosis';'pts_var','total_pts';'ppts_var','pos_pts';'npts_var','neg_pts';'zpts_var','zero_pts'})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% kprstats - Print statistics to Cantata variables
%
%  DESCRIPTION
%
%
%
%  EXAMPLES
%
%  "SEE ALSO"
%
%  RESTRICTIONS
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

function varargout = kkprstats(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kkprstats(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i', '__input';'wmin_var', 'width_min';'wmax_var', 'width_max';'hmin_var', 'height_min';'hmaxvar', 'height_max';'dmin_var', 'depth_min';'dmax_var', 'depth_max';'tmin_var', 'time_min';'tmax_var', 'time_max';'emin_var', 'elem_min';'emax_var', 'elem_max';'minval_var', 'val_min';'maxval_var', 'val_max';'mean_var', 'mean';'wsize_var', 'width_size';'var_var', 'variance';'hsize_var', 'height_size';'sd_var', 'std_dev';'dsize_var', 'depth_size';'rms_var', 'rms';'tsize_var', 'time_size';'psum_var', 'psum';'esize_var', 'elem_size';'nsum_var', 'nsum';'sum_var', 'sum';'skew_var', 'skew';'kur_var', 'kurtosis';'pts_var', 'total_pts';'ppts_var', 'pos_pts';'npts_var', 'neg_pts';'zpts_var', 'zero_pts'};
maxval={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
minval={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
istoggle=[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
was_set=istoggle * 0;
paramtype={'InputFile','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String','String'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=0; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(0);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
callKhoros([w 'kprstats"  '],Inputs,narglist);


function [proj, angles, blk, projinfo] = BatchReadXim(datafolder,varargin)
% [proj, angle, blk, projinfo] = BatchReadXim(foldername, varargin)
% Varian .xim files exported from TrueBeam on-board imager/EPID, Halcyon imager
% .xim files are exported under service mode, as a single .zip file
% Once unzipped, all relevant files are organized under the same root folder
% Input:
%       datafolder    : root folder of the upzipped files,
%                       or the subfolder Acquisitions where all xim files are stored
%       thd           : 0(default), no motion lag correcion
%                       thd,  motion correcion threadhold
% Output:
%       proj          : .xim pixel_images frame by frame
%       angles        : KVSourceRtn at each frame
%       projinfo      : proj_*.xim info cell arrays
% Date: 2020-04-11
% Author: Yi Du, yi.du@hotmail.com

thd = 0;
if(nargin == 2)
    thd = varargin{1};
end

read_xim_info = 0;
if(nargout==4)
    read_xim_info = 1;
end

fprintf("Dataset in: %s \n", datafolder);

ximfilelist = dir([datafolder filesep '**' filesep 'Proj_*.xim']);

proj = [];
angles = [];

%
proj_no = length(ximfilelist);
%
count = 1;
for ii = 1:proj_no
	ximfilename = fullfile(ximfilelist(ii).folder, ximfilelist(ii).name);
    if(~mod(ii,50))
        fprintf("Loading: %d / %d \n", ii, proj_no);
    end
	[page, rtn] = mexReadXim(ximfilename);
	if(~isempty(page))
        % load first page
        if(count==1)
            projinfo{count} = ReadXim(ximfilename, 0);
    		angles(count) = rtn;
        	proj(:,:,count) = double(page');
            count = count + 1;
        else
            % proj info
            if(read_xim_info)
                projinfo{count} = ReadXim(ximfilename, 0);
            end

            % whether threshold is applied
            if(thd)
                if(abs(rtn-angles(end))>thd)
                    angles(count) = rtn;
                    proj(:,:,count) = double(page');
                    count = count + 1;
                else
                    continue;
                end
            else
                angles(count) = rtn;
                proj(:,:,count) = double(page');
                count = count + 1;
            end
        end
	end
end
fprintf("Loading complete \n");

%% Load Blk
tag_bowtie = strfind(projinfo{1}.properties.KVCollimatorShape, 'None');
% wi Bowtie1
if(isempty(tag_bowtie))
    blkfilestr = dir([datafolder filesep 'Calibrations' filesep 'AIR-*' filesep '**' filesep 'FilterBowtie.xim']);
else
    blkfilestr = dir([datafolder filesep 'Calibrations' filesep 'AIR-*' filesep '**' filesep 'Filter.xim']);
end

blkfilename = fullfile(blkfilestr.folder, blkfilestr.name);
blk = mexReadXim(blkfilename);
blk = double(blk');

%% Don't know why mAs correction doesn't work
%{
% mAs for projection sampling
mAsproj = projinfo{1}.properties.KVMilliAmperes * projinfo{1}.properties.KVMilliSeconds;
% mAs for blk field
blkinfo = ReadXim(blkfilename, 0);
mAsblk = blkinfo.properties.KVMilliAmperes * blkinfo.properties.KVMilliSeconds;
% mAs output correction
gain = mAsproj/mAsblk;
%}
% Emperical value
gain = 2.5;
blk = blk * gain;
% denoising blk
blk = medfilt2(blk, [5 5]);

end


function dicomrt_contourslice(inputdose,dose_xmesh,dose_ymesh,dose_zmesh,axis,plane,type,VOI,voi2plot)
% dicomrt_contourslice(inputdose,dose_xmesh,dose_ymesh,dose_zmesh,axis,plane,type,VOI,voi2plot)
%
% Show 3D dose distributions.
%
% inputdose contains the 3D dose distribution
% inputdose can be an rtplan generated or a monte carlo generated 3D dose distribution or a dose difference.
%
% dose_xmesh,dose_ymesh,dose_zmesh are x-y-z coordinates of the center of the dose-pixel
% axis is the plane to be used for slicing the 3D dose matrix (can be x,y or z. Key insensitive).
% plane is a vector containing the slice number of the slices to be displayed
% type OPTIONAL parameter which determines whether display the slices in the same
%      plot (type==0) or in independent subplots (type~=0)
% VOI is an OPTIONAL cell array which contain the patients VOIs as read by dicomrt_loadVOI
% voi2plot is an OPTIONAL vector pointing to the number of VOIs to be displayed
%
% Example:
%
% dicomrt_contourslice(A,xmesh,ymesh,zmesh,'z',[10 15 25], A_voi,[1 2 3])
%
% plot a figure that contains: 3 plots with inputdose sliced at voxel number 10 15 and 25
% All plots show also VOIs # 1 2 and 3.
%
% See also dicomrt_loadmcdose
%
% Copyright (C) 2002 Emiliano Spezi (emiliano.spezi@physics.org)

% Check number of argument and set-up some parameters and variables
error(nargchk(6,9,nargin))

% Check type
if exist('type')~=1
    type=0;
end

% Check case and set-up some parameters and variables
[dose_temp,type_dose,doselabel,PatientPosition]=dicomrt_checkinput(inputdose);
dose=dicomrt_varfilter(dose_temp);

% Check axis
if ischar(axis) ~=1
    error('dicomrt_contourslice: Axis is not a character. Exit now!')
elseif axis=='x' | axis=='X'
    dir=1;
elseif axis=='y' | axis=='Y'
    dir=2;
elseif axis=='z' | axis=='Z'
    dir=3;
else
    error('dicomrt_contourslice: Axis can only be X Y or Z. Exit now!')
end

% rebuilding mesh matrices
[rtdose_xmesh,rtdose_ymesh,rtdose_zmesh]=dicomrt_rebuildmatrix(dose_xmesh,dose_ymesh,dose_zmesh);

% Initialize plot parameters:
color = char('r','g','b','c','m','y','k','w');
line = char('-','--',':','-.');
marker = char('+','o','*','x','s','d','^');
width = [0.5,1.0,1.5,2.0,2.5,3.0];

% Rendering 3D dose
figure
set(gcf,'Name',['dicomrt_contourslice: ',inputname(1)]);
hold

if length(plane)==1
    if dir==1
        p = slice(rtdose_xmesh,rtdose_ymesh,rtdose_zmesh,dose,dose_xmesh(plane),[],[]);
    elseif dir==2
        p = slice(rtdose_xmesh,rtdose_ymesh,rtdose_zmesh,dose,[],dose_ymesh(plane),[]);
    elseif dir==3
        p = slice(rtdose_xmesh,rtdose_ymesh,rtdose_zmesh,dose,[],[],dose_zmesh(plane));
    else
        error('dicomrt_contourslice: Error while rendering. Exit now');
    end
    view(30,30);
    set(p,'FaceColor','interp','EdgeColor','none','DiffuseStrength',.8);
    xlabel('X axis (cm)')
    ylabel('Y axis (cm)')
    zlabel('Z axis (cm)')
    if exist('VOI')==1 % VOI is defined
        dicomrt_rendervoi(VOI,voi2plot,1,1,1);
    end
    grid on
else
    if type~=0
        for i=1:length(plane)
            subplot(2,round((length(plane)/2)),i);
            if dir==1
                p = slice(rtdose_xmesh,rtdose_ymesh,rtdose_zmesh,dose,dose_xmesh(plane(i)),[],[]);
                set(p,'FaceColor','interp','EdgeColor','none','DiffuseStrength',.8);
            elseif dir==2
                p = slice(rtdose_xmesh,rtdose_ymesh,rtdose_zmesh,dose,[],dose_ymesh(plane(i)),[]);
                set(p,'FaceColor','interp','EdgeColor','none','DiffuseStrength',.8);
            elseif dir==3
                p = slice(rtdose_xmesh,rtdose_ymesh,rtdose_zmesh,dose,[],[],dose_zmesh(plane(i)));
                set(p,'FaceColor','interp','EdgeColor','none','DiffuseStrength',.8);
            else
                error('dicomrt_contourslice: Error while rendering. Exit now');
            end
            view(30,30);
            set(p,'FaceColor','interp','EdgeColor','none','DiffuseStrength',.8);
            xlabel('X axis (cm)')
            ylabel('Y axis (cm)')
            zlabel('Z axis (cm)')
            if exist('VOI')==1 % VOI is defined
                hold
                dicomrt_rendervoi(VOI,voi2plot,1,1,1);
            end
        end
    else
        for i=1:length(plane)
            if dir==1
                p = slice(rtdose_xmesh,rtdose_ymesh,rtdose_zmesh,dose,dose_xmesh(plane(i)),[],[]);
                set(p,'FaceColor','interp','EdgeColor','none','DiffuseStrength',.8);
            elseif dir==2
                p = slice(rtdose_xmesh,rtdose_ymesh,rtdose_zmesh,dose,[],dose_ymesh(plane(i)),[]);
                set(p,'FaceColor','interp','EdgeColor','none','DiffuseStrength',.8);
            elseif dir==3
                p = slice(rtdose_xmesh,rtdose_ymesh,rtdose_zmesh,dose,[],[],dose_zmesh(plane(i)));
                set(p,'FaceColor','interp','EdgeColor','none','DiffuseStrength',.8);
            else
                error('dicomrt_contourslice: Error while rendering. Exit now');
            end
            view(30,30);
            set(p,'FaceColor','interp','EdgeColor','none','DiffuseStrength',.8);
            xlabel('X axis (cm)')
            ylabel('Y axis (cm)')
            zlabel('Z axis (cm)')
        end
        if exist('VOI')==1 % VOI is defined
            dicomrt_rendervoi(VOI,voi2plot,1,1,1);
        end
        grid on
    end
end


function savewav(y,filename,fs)
%SAVEWAV saves floating point audio data in a *.wav with 32bit precision
%
%   Usage: savewav(y,filename,fs)
%
%   Input parameters:
%       y         - audio data in floating point [NFrames x NChannels]
%       filename  - name of *.wav file (file extension will NOT be added
%                   automatically)
%       fs        - sample rate / Hz
%
%   Since wavwrite has been removed in MATLAB 2015b and audiowrite does not
%   support audio data with a channel number higher than 256, this is an
%   alternative to save massive multichannel. This function does only support
%   floating point data and saves it 32bit floating point precision.
%
%   See also: audiowrite, audioread, fwrite

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

%% Input Check
narginchk(3,3);
if ~isfloat(y)
  error('%s: only floating point supported', upper(mfilename));
end
if ~ndims(y) > 2
  error('%s: only audio data with [NFrames x NChannels] supported', ...
    upper(mfilename));
end

%%
[NFrames, NChannels] = size(y);  % Number of Frames and Channels
NSamples = NFrames*NChannels;  % total Samples
Nbits = 32;  % 32 floating point
Nbyte = 4;  % bytes per Samples

fileID = fopen(filename,'w', 'ieee-le');  % open wav-file (little Endian)

fwrite(fileID, 'RIFF', 	'char*1');  % RIFF header
% number of bytes following (chunk size)
Noverallbytes = 4 + 4; % for 'WAVE' and 'fmt '
Noverallbytes = Noverallbytes + 4;  % for the number of bytes in 'fmt '
Noverallbytes = Noverallbytes + 16; % bytes in 'fmt '
Noverallbytes = Noverallbytes + 12; % for 'fact'
Noverallbytes = Noverallbytes + 8 + Nbyte*NSamples;  % 'data' chunk
fwrite(fileID, Noverallbytes, 'uint32');

fwrite(fileID, 'WAVE', 	'char*1');  % WAVE header

%% fmt chunk
fwrite(fileID, 'fmt ', 	'char*1');  % mind the whitespace!
fwrite(fileID, 16, 	'uint32');  % number of bytes following (chunk size)
fwrite(fileID, 3, 'uint16');  % data type (3 for float)
fwrite(fileID, NChannels, 'uint16');  % number of channels
fwrite(fileID, fs, 'uint32');  % samplerate
fwrite(fileID, fs * NChannels * Nbyte, 'uint32');  % bytes per second
fwrite(fileID, NChannels * Nbyte, 'uint16');  % frame size
fwrite(fileID, Nbits, 'uint16');  % bits per sample

%% fact chunk (floating point only)
fwrite(fileID, 'fact', 	'char*1');
fwrite(fileID, 4, 	'uint32');  % number of bytes following (chunk size)
fwrite(fileID, NFrames, 	'uint32');  % number of frames

%% data chunk
fwrite(fileID, 'data', 	'char*1');
fwrite(fileID, Nbyte*NSamples, 'uint32');  % number of bytes following (chunk size)
y = y.';  % prepare data for interlacing
fwrite(fileID, y(:), 'float32');  % interlaced data

%%
fclose(fileID);

end