function [varargout]=febioOutputView(pathName,febio_spec)

% function [hf]=febioOutputView(pathName,febio_spec)
% ------------------------------------------------------------------------
%
%
%
% ------------------------------------------------------------------------

%%
% optionStruct=[];

%%
fontSize=15;

%%
hf=cFigure; %Open figure

%%

V=febio_spec.Geometry.Nodes{1}.node.VAL;
E_cell=febio_spec.Geometry.Elements;

%%

nodeDataCellAll=febio_spec.Output.logfile.node_data;

nodalDisplacementAll=zeros(size(V));
for q=1:1:numel(nodeDataCellAll)
    dataTypeString=nodeDataCellAll{q}.ATTR.data;
    if strcmp(dataTypeString,'ux;uy;uz')
        [~,nodalDisplacementAll,~]=importFEBio_logfile(fullfile(pathName,nodeDataCellAll{q}.ATTR.file)); %Import data

        nodalDisplacementAll=nodalDisplacementAll(:,2:end,:);
        sizImport=size(nodalDisplacementAll);
        sizImport(3)=sizImport(3)+1;
        dataArray_n=zeros(sizImport);
        dataArray_n(:,:,2:end)=nodalDisplacementAll;
        nodalDisplacementAll=dataArray_n;
        U_end=nodalDisplacementAll(:,:,end);
        U_mag=sqrt(sum(U_end(:,3).^2,2));
        V_def=V+U_end;
    end
end
nodalDisplacementMagnitudeAll=sqrt(sum(nodalDisplacementAll.^2,2));

hf.UserData.febioOutputView.colorData=[];
hf.UserData.febioOutputView.pathName=pathName;
hf.UserData.febioOutputView.febio_spec=febio_spec;

%%
% Create basic view and store graphics handle to initiate animation

hf.UserData.febioOutputView.ht=gtitle(' - '); %Figure axis title

handleSet=gobjects(1,numel(E_cell));
for q=1:1:numel(E_cell)

    elementsNow=febio_spec.Geometry.Elements{q}.elem.VAL;
    elementTypeNow=febio_spec.Geometry.Elements{1}.ATTR.type;

    if any(strcmp(elementTypeNow,{'tri3','tri6','tri7','quad4','quad8'})) %Surface elements
        facesNow=elementsNow;
    else %Solid elements
        facesNow=element2patch(elementsNow);
    end

    handleSet(q)=gpatch(facesNow,V,U_mag,'k',1); %Add graphics object to animate
    handleSet(q).FaceColor='interp';
end

axisGeom(gca,fontSize);
colormap(gjet(250)); colorbar;
cLim=[min(nodalDisplacementMagnitudeAll(:)) max(nodalDisplacementMagnitudeAll(:))];
if abs(cLim(2)-cLim(1))<eps(1)
    cLim(1)=cLim(1)-1;
    cLim(2)=cLim(2)+1;
end
hf.UserData.febioOutputView.cLim=cLim;
caxis(cLim);
VV=[V;V_def];
axis([min(VV(:,1)) max(VV(:,1)) min(VV(:,2)) max(VV(:,2)) min(VV(:,3)) max(VV(:,3))]); %Set axis limits statically
camlight headlight;

hf.UserData.febioOutputView.handleSet=handleSet;

%%

outputDataCellAll=febio_spec.Output.logfile.node_data;
if isfield(febio_spec.Output.logfile,'element_data')
    outputDataCellAll=[outputDataCellAll febio_spec.Output.logfile.element_data];
end

popUpString=cell(1,1);
%Initialize with displacement magnitude
popUpString{1}='um';
hf.UserData.febioOutputView.data.um=nodalDisplacementMagnitudeAll;

for q=1:1:numel(outputDataCellAll)

    dataTypeString=outputDataCellAll{q}.ATTR.data;
    dataTypeSet=strsplit(dataTypeString,';');

    [timeVec,dataArray,~]=importFEBio_logfile(fullfile(pathName,outputDataCellAll{q}.ATTR.file)); %Import data
    dataArray=dataArray(:,2:end,:);
    sizImport=size(dataArray);
    sizImport(3)=sizImport(3)+1;
    dataArray_n=zeros(sizImport);
    dataArray_n(:,:,2:end)=dataArray;
    dataArray=dataArray_n;

    for qSet=1:1:numel(dataTypeSet)
        dataTypeStringNow=dataTypeSet{qSet};
        popUpString{end+1}=dataTypeStringNow;
        dataNow=dataArray(:,qSet,:);
        switch dataTypeStringNow
            case 'J' %Volume ratio
                dataNow(:,:,1)=1; %set initial to ones
        end
        hf.UserData.febioOutputView.data.(dataTypeStringNow)=dataNow;
    end

end

timeVec=[0; timeVec(:)]; %Time

%%

backGroundColor=hf.Color;
if strcmp(backGroundColor,'k')
    textColor='w';
elseif size(backGroundColor,2)==3
    grayLevels=linspace(0,1,100);
    [~,indMax]=max(abs(grayLevels-mean(double(backGroundColor))));
    textColor=grayLevels(indMax)*ones(1,3);
else
    textColor='k';
end

hPop = uicontrol(hf,'Style','popupmenu','String',popUpString,...
    'BackgroundColor',backGroundColor,'FontSize',fontSize,'FontWeight','normal','Units','Points','ForegroundColor',textColor,...
    'Callback',@(a,b)setColorData(a,b,{hf}));

hFunc=get(hf,'ResizeFcn');

if iscell(hFunc)
    warning('gtitle replaced the ResizeFcn function. Specify your ResizeFcn in the form @(h,e)figResize(h,e,c) to avoid this behavior');
    set(hf,'ResizeFcn',@(a,b)figResize(a,b,{hf,hPop}));
else
    if isempty(hFunc)
        set(hf,'ResizeFcn',@(a,b)figResize(a,b,{hf,hPop}));
    else
        set(hf,'ResizeFcn',@(a,b)(cellfun(@(x)feval(x,a,b),{hFunc,@(a,b)figResize(a,b,{hf,hPop})})));
    end
end

figResize([],[],{hf,hPop});

%%

clear animStruct;

% Set up animation features
animStruct.Time=timeVec; %The time vector
propertySetVert=repmat({'Vertices'},1,numel(handleSet));
propertySetCData=repmat({'CData'},1,numel(handleSet));

for qt=1:1:size(nodalDisplacementAll,3) %Loop over time increments
    U_now=nodalDisplacementAll(:,:,qt); %Current displacement
    V_def=V+U_now; %Current nodal coordinates
    d1=repmat({V_def},1,numel(handleSet));

    %Initialize animation structure with nodal coordinate and displacement animation
    animStruct.Handles{qt}=[handleSet]; %Handles of objects to animate
    animStruct.Props{qt}={propertySetVert{:}}; %Properties of objects to animate
    animStruct.Set{qt}={d1{:}}; %Property values for to set in order to animate

    %Add colordata animations for faces
    colorData=nodalDisplacementMagnitudeAll(:,:,qt);

    d2={};
    for qs=1:1:numel(handleSet)
        colorDataNow=colorData;
%         if size(colorData,1)==size(V,1)
%             f=handleSet(qs).Faces;
%             [colorDataNow]=vertexToFaceMeasure(f,colorData);
%         end
        f=handleSet(qs).Faces;
        if size(colorData,1)==size(f,1)
            [colorDataNow]=faceToVertexMeasure(f,V,colorData);
        end
        d2{qs}=colorDataNow;
    end
    animStruct.Handles{qt}(1:2*numel(handleSet))=[handleSet handleSet]; %Handles of objects to animate
    animStruct.Props{qt}(end+1:end+numel(propertySetCData))={propertySetCData{:}}; %Properties of objects to animate
    animStruct.Set{qt}(end+1:end+numel(propertySetCData))={d2{:}}; %Property values for to set in order to animate
end

anim8(hf,animStruct); %Initiate animation feature
drawnow;

%%

if nargout>0
    varargout{1}=hf;
end

end

%%

function figResize(~,~,inputCell)

hf=inputCell{1};
hPop=inputCell{2};
unitsNow=hf.Units;
hf.Units='Points';

figPosition=hf.Position;
textBoxWidth=round(figPosition(3)/6);
textBoxHeight=hPop.Extent(4).*ceil(hPop.Extent(3)./figPosition(3));
textPosition=[0 figPosition(4)-textBoxHeight textBoxWidth textBoxHeight];
hPop.Position = textPosition;

hf.Units=unitsNow;

end

%%

function setColorData(source,~,inputCell)
hf=inputCell{1};
indexValue = source.Value;
stringSet = source.String;

dataTypeStringNow = stringSet{indexValue};
hf.UserData.febioOutputView.ht.String=dataTypeStringNow;

hf.UserData.febioOutputView.colorData=hf.UserData.febioOutputView.data.(dataTypeStringNow);

updateFaceColor(hf);

caxis(hf.UserData.febioOutputView.cLim);
drawnow;

end

%%

function updateFaceColor(hf)

for qt=1:1:numel(hf.UserData.anim8.animStruct.Time)
    cLim=[];
    for qh=numel(hf.UserData.febioOutputView.handleSet)+1:1:2*numel(hf.UserData.febioOutputView.handleSet)
        c=hf.UserData.febioOutputView.colorData(:,:,qt);
        d2=repmat({c},1,numel(hf.UserData.febioOutputView.handleSet));
        hf.UserData.anim8.animStruct.Set{qt}{qh}=d2{:}; %Property values for to set in order to animate

        if isempty(cLim)
            cLim=[min(c(:)) max(c(:))];
        else
            cLim(1)=min(min(c(:)),cLim(1));
            cLim(2)=max(max(c(:)),cLim(2));
        end
    end
end

if abs(cLim(2)-cLim(1))<eps(1)
    cLim(1)=cLim(1)-1;
    cLim(2)=cLim(2)+1;
end
hf.UserData.febioOutputView.cLim=cLim;

%Briefly toggle slider to trigger anim8 redraw event
jSlider=hf.UserData.anim8.sliderHandles{1};
v=get(jSlider,'Value');
if v>1
    set(jSlider,'Value',v-1);
    set(jSlider,'Value',v);
else
    set(jSlider,'Value',v+1);
    set(jSlider,'Value',v);
end

end

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


function y = diag( v, k )

%Disciplined convex/geometric programming information for DIAG:
%   DIAG imposes no convexity restrictions on its arguments.

switch nargin,
    case 0,
        error( 'Not enough arguments.' );
    case 1,
        k = 0;
    case 2,
        if ~isnumeric( k ) || k ~= floor( k ),
            error( 'Second argument must be an integer.' );
        end
end

s = size( v );
if length( s ) ~= 2,
    error( 'First input must be 2D.' );
end

if k < 0,
    absk = -k;
    roff = absk;
    coff = 0;
else
    absk = +k;
    roff = 0;
    coff = absk;
end

if any( s == 1 ),
    nn = prod( s );
    nel = nn + roff + coff;
    y = sparse( roff + 1 : roff + nn, coff + 1 : coff + nn, v, nel, nel );
elseif roff >= s(1) || coff >= s(2),
    y = sparse( 0, 1 );
else
    nel = min( s(1) - roff, s(2) - coff );
    nv = roff + ( coff - 1 ) * s(1) + ( 1 : nel ) * ( s(1) + 1 );
    y = reshape( cvx_subsref( v, nv ), nel, 1 );
end

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


function javaview(surface_handle, filename, destination, source, open_html )
% Syntax:
%    javaview(surface_handle, filename, destination, source, open_html)
%
% Description:
%    This function generates output files for Javaview (www.javaview.de).
%    It allows to display and interact with 3d-Graph not only in the
%    javaview engine, but also in html-Documents. See javaview
%    documentation for more information about how to interact.
%
%    There are 4 output files:
%        a) first file (.stl)is generated by the surf2stl function (Many
%           thanks to Bill McDonald, this function is availabe at the MCFE
%           and was pick of the week on June 5th, 2009)
%        b) the second and third file (jvx) and (jvd) are input files for
%           javaview and are generated by mupad. The jvx-file describes the
%           3d-graph, the jvd file descrribes scene options
%        c) the last file is an html file, were the 3d-Graph is displayed
%
%   Because the applet in html file need the javaruntime for javaview the
%   jars directory from the "source" (input parameter) directory is copied
%   to the destination folder.
%   open_html is true or false. When it is true your standard webbrowser is
%   opened and you will see the surface in the html file.
%
%   Parameter:
%       1. surface_handle - handle to surface graph
%       2. filename       - filename for *.stl, .jvx, *.jvd, *.html
%       3. destination    - folder where all files are saved
%       4. source         - source folder of jars (see jvaview installation path)
%       5. open_html      - shows the result html in your standard webbrowser
%
%  Sorry for the work around with the handmade copy and paste when to
%  generate the javaview files. But evalin(symengine, ...) does not produce
%  the required files.
%
% Online Example:
%       http://wwwpub.zih.tu-dresden.de/~s9034647/peaksurface.html
%
% Example:
%       [X,Y,Z] = peaks(30);
%       h = surf(X,Y,Z);
%       javaview(h, 'peaksurface', 'C:\Users\sk\Desktop\3d-PDF\jvtest', 'C:\Program Files (x86)\JavaView\jars', 1)
%
%
% Bugs and suggestions:
%    Please send to Sven Koerner: koerner(underline)sven(add)gmx.de
%
% You need to download and install first:
%    www.javaview.de
%    http://blogs.mathworks.com/pick/2009/06/5/writing-to-stl-files/
%    Symbolic Math Toolbox with MuPad
%
%
% License to use and modify this code is granted freely to all interested, as long as the original author is
% referenced and attributed as such. The original author maintains the right to be solely associated with this work.
%
% Programmed by Sven Koerner: koerner(underline)sven(add)gmx.de
% Date: 2010/04/20

%% Test - Configuration parameter
% open_html       = 0;
% filename        = 'jvfile';
% source          = 'C:\Program Files (x86)\JavaView\jars';  % source folder of jars (installed with javaview)
% destination     = 'C:\Users\sk\Desktop\3d-PDF\jvtest';     % destination folder

%% Get a couple of surface and axis properties
X               = get(surface_handle, 'XData');
Y               = get(surface_handle, 'YData');
Z               = get(surface_handle, 'ZData');

ah              = get(get(surface_handle,'Parent'));
x_lb            = get(ah.XLabel, 'String');
y_lb            = get(ah.YLabel, 'String');
z_lb            = get(ah.ZLabel, 'String');
header          = get(ah.Title,  'String');

%% Generate stl-File
stl_file        = [destination '\' filename '.stl'];
surf2stl(stl_file,X,Y,Z);

%% String generation
p1              =  strrep(stl_file,'\','\\');
p2              =  [filename,'.jvx'];
p3              =  [filename,'.jvd'];

mupad_str1 = ['plot(plot::SurfaceSTL("' p1 ' "), Scaling = Unconstrained, FillColorType=Rainbow, XLinesVisible = FALSE, YLinesVisible = FALSE,MeshVisible = FALSE, Header="' header ...
             '", XAxisTitle=" ' x_lb '", YAxisTitle=" ' y_lb '",  ZAxisTitle="' z_lb '", GridVisible=FALSE, Lighting = None, OutputFile="' p2 '")'];
mupad_str2 = ['plot(plot::SurfaceSTL("' p1 ' "), Scaling = Unconstrained, FillColorType=Rainbow, XLinesVisible = FALSE, YLinesVisible = FALSE,MeshVisible = FALSE, Header="' header ...
             '", XAxisTitle=" ' x_lb '", YAxisTitle=" ' y_lb '",  ZAxisTitle="' z_lb '", GridVisible=FALSE, Lighting = None, OutputFile="' p3 '")'];

%% MUPAD
clipboard('copy',[mupad_str1 ': ' mupad_str2]);
mpnb = mupad;

%% now a strange workaround
% evalin(symengine,mupad_str1);  % --> actually this doesn't work
% evalin(symengine,mupad_str2);  % --> actually this doesn't work
clc;
reply = input('please press "strg-v" in open mupad notebook after the [-bracket (the text need to be red in mupad nb)! Then press first enter in mupad and then any key in matlab prompt here! ', 's');
try
    movefile([cd '\' filename '.jvd'], destination);
    movefile([cd '\' filename '.jvx'], destination);
catch
    disp('Have you really pasted the clipboard to mupad and press enter? - Try again')
end;
close(mpnb);

%% Generate html-File
fid         =   fopen([destination '\' filename '.html' ], 'w');
html_str    =   {'<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>';
                    '';
                    '';
                    '<meta generator="JavaView v.3.95.001"/>';
                    '<meta date="Fri Sep 15 20:45:51 CEST 1978"/>';
                    ['<title>' filename '.jvx</title>'];
                    '';
                    '';
                    ['
 Applet shows ' filename  '.jvx (select a point with "h")
'];
                    '<applet height="512" archive="http://www.javaview.de/demo/jars/javaview.jar" width="640" code="http://www.javaview.de/demo/javaview.class">';
                    ['<param name=model value=" ' filename '.jvx">'];
                    ['<param name=displayFile value=" ' filename '.jvd">'];
                    '<param name=panel value="material">';
                    '</applet>';
                    '';
                    ''};
                for i=1:1:size(html_str,1)
                    fprintf(fid, char(html_str(i,1)));
                end;

fclose(fid);

%% Copy source for applet
copyfile(source, [destination '\jars'] );

%% open the html result in standard html-viewer (e.g. ie)
if open_html
    winopen([destination '\' filename '.html' ]);
end;


 function ob = fatrix2_vertcat(blocks, varargin)
%function ob = fatrix2_vertcat(blocks, [options])
%| vertcat: B = [A1; A2; ...]
%|
%| in
%|	blocks	{cell}	cell array of the (fatrix2) blocks
%|			all blocks must have same idim and imask
%|
%| option
%|	'dim_cat'	dimension along which to concatenate odims
%|			all other odims must match
%|			default is ndim+1 if *all* dimensions match
%|			and imask is not full 1D.
%|			otherwise default is 1
%| out
%|	ob = [A1; A2; ...]
%|
%| If all input blocks have the same odim and have a handle_forw_block
%| then output could also have a handle_forw_block (todo)

arg.dim_cat = []; % default is to infer from odims
arg = vararg_pair(arg, varargin);
arg.out_1d = false;

MM = numel(blocks);

fatrix2_vertcat_check_all_fatrix2(blocks)
fatrix2_vertcat_check_idim(blocks)

[odims_same, odims] = fatrix2_same_odims(blocks);
if ~odims_same % columnize
	warn 'mismatched odims so reverting to single column output'
	arg.out_1d = true;
	for mm=1:MM
		odims{mm} = prod(odims{mm});
	end
end

arg.blocks = blocks;

if isempty(arg.dim_cat)
	b1 = blocks{1};
	if numel(b1.idim) == 1 && size(b1,2) == b1.idim % full 1d input
		arg.dim_cat = 1;
	elseif odims_same
		arg.dim_cat = 'next';
	else
		arg.dim_cat = 1;
	end
end

if streq(arg.dim_cat, 'next')
	if ~odims_same
		fail 'dim_cat ''next'' works only if all odims same'
	end
	odim = [odims{1} MM];
	arg.dim_cat = numel(odim);
else
	id_check = 1:numel(odims{1});
	id_check(arg.dim_cat) = []; % odim must match except dim_cat
	for mm=1:MM
		if ~isequal(odims{mm}(id_check), odims{1}(id_check))
			fail('all blocks must have same odim (except dim_cat)')
		end
	end

	tmp = cell2mat(odims); % [MM ndim]
	tmp = sum(tmp(:,arg.dim_cat));
	odim = odims{1}; odim(arg.dim_cat) = tmp;
end

if fatrix2_vertcat_omask_all_empty(blocks)
	omask = []; % all empty omasks
else
	tmp = cell(MM,1);
	for mm=1:MM
		tmp{mm} = fatrix2_omask_array(blocks{mm});
		if arg.out_1d
			tmp{mm} = tmp{mm}(:);
		end
	end
	omask = cat(arg.dim_cat, tmp{:});
end

arg.mat2cell_arg = fatrix2_vertcat_mat2cell_arg(odims, arg.dim_cat, odim);

% build object
ob = fatrix2('arg', arg, 'caller', 'fatrix2_vertcat', ...
	'idim', blocks{1}.idim, 'imask', blocks{1}.imask, ...
	'odim', odim, 'omask', omask, ...
	'sparse', @fatrix2_vertcat_sparse, ...
	'abs', @fatrix2_vertcat_abs, 'power', @fatrix2_vertcat_power, ...
	'forw', @fatrix2_vertcat_forw, 'back', @fatrix2_vertcat_back);

% fatrix2_vertcat_check_all_fatrix2()
function fatrix2_vertcat_check_all_fatrix2(blocks)
if ~iscell(blocks)
	fail('blocks must be cell array, not "%s"', class(blocks))
end
for mm=1:numel(blocks);
	if ~isa(blocks{mm}, 'fatrix2')
		fail('vertcat requires all fatrix2; use Gmatrix if needed')
	end
end

% fatrix2_vertcat_check_idim()
% check compatibility of input dimensions
function fatrix2_vertcat_check_idim(blocks)
b1 = blocks{1};
for mm=2:numel(blocks);
	bm = blocks{mm};
	if size(bm,2) ~= size(b1,2)
		fail('all blocks must have same #cols')
	end
	if ~isequal(bm.idim, b1.idim)
		fail('all blocks must have same idim')
	end
	if ~isequal(bm.imask, b1.imask)
		fail('all blocks must have same imask')
	end
end

% fatrix2_vertcat_forw(): y = A * x
function y = fatrix2_vertcat_forw(arg, x)

MM = numel(arg.blocks);
yy = cell(MM,1);
for mm=1:MM
	bi = arg.blocks{mm};
	yy{mm} = fatrix2_do_forw(bi, x);
%	yy{mm} = bi.handle_forw(bi.arg, x);
%	if bi.scale ~= 1
%		yy{mm} = bi.scale * yy{mm};
%	end
	if arg.out_1d
		yy{mm} = yy{mm}(:);
	end
end

y = cat(arg.dim_cat, yy{:});

% fatrix2_vertcat_back(): x = A' * y
function x = fatrix2_vertcat_back(arg, y)

yy = mat2cell(y, arg.mat2cell_arg{:});

MM = length(arg.blocks);
for mm=1:MM
	bi = arg.blocks{mm};
	if arg.out_1d
		yy{mm} = reshape(yy{mm}, [bi.odim 1]);
	end
	tmp = fatrix2_do_back(bi, yy{mm});
%	tmp = bi.handle_back(bi.arg, yy{mm});
%	if bi.scale ~= 1
%		tmp = conj(bi.scale) * tmp;
%	end
	if mm == 1
		x = tmp;
	else
		x = x + tmp;
	end
end

% fatrix2_vertcat_abs(): abs(ob)
function ob = fatrix2_vertcat_abs(ob)

MM = numel(ob.arg.blocks);
for mm=1:MM
	ob.arg.blocks{mm} = abs(ob.arg.blocks{mm});
end

% fatrix2_vertcat_power(): ob.^sup
function ob = fatrix2_vertcat_power(ob, sup)

MM = numel(ob.arg.blocks);
for mm=1:MM
	ob.arg.blocks{mm} = (ob.arg.blocks{mm}) .^ sup;
end

% fatrix2_vertcat_mat2cell_arg()
function mat2cell_arg = fatrix2_vertcat_mat2cell_arg(odims, dim_cat, odim)

MM = numel(odims); % odims is cell(MM,1)

if dim_cat == numel(odims{1}) + 1
	for id = 1:numel(odims{1})
		mat2cell_arg{id} = odim(id);
	end
	mat2cell_arg{dim_cat} = ones(MM,1);
	tmp1 = 1:prod(odim);
	tmp1 = reshape(tmp1, odim);
	tmp2 = mat2cell(tmp1, mat2cell_arg{:});
else
	for mm=1:MM
		odim_m = odims{mm};
		if mm==1
			for id = 1:numel(odim_m)
				if id ~= dim_cat
					mat2cell_arg{id} = odim_m(id);
				end
			end
		end
		mat2cell_arg{dim_cat}(mm) = odim_m(dim_cat);
	end
end

% fatrix2_vertcat_omask_all_empty()
% see if all blocks have empty omask (often this will be the case)
function allempty = fatrix2_vertcat_omask_all_empty(blocks)
allempty = true;
for mm=1:numel(blocks)
	allempty = allempty & isempty(blocks{mm}.omask);
end

% fatrix2_vertcat_sparse(): S = sparse(A)
function S = fatrix2_vertcat_sparse(ob)
arg = ob.arg;
S = sparse([]);
MM = numel(arg.blocks);
S = cell(MM,1);
for mm=1:MM
	S{mm} = sparse(arg.blocks{mm});
end
S = cat(1, S{:});

%{
% fatrix2_vertcat_gram()
% todo: this would be useful if W is empty or (block) diagonal
% because it would save memory, but it is extra complexity
function [T, reuse] = fatrix2_vertcat_gram(ob, W, reuse, varargin)

blocks = ob.arg.blocks;
T = cell(size(blocks));
for mm=1:length(blocks)
	A = blocks{mm};
	if isnumeric(A)
		if isempty(W)
			T{mm} = A' * A;
		else
			warn 'todo: this may not work, need piece of W!'
			T{mm} = A' * W * A;
		end
	else
		if isempty(W)
			T{mm} = build_gram(A, [], reuse, varargin{:});
		else
			if isvar('W.arg.blocks{mm}')
				T{mm} = build_gram(A, W.arg.blocks{mm}, ...
					reuse, varargin{:});
			else
				fail('fatrix2_vertcat_gram needs block diag W')
			end
		end
	end
end
T = fatrix_block_sum(T{:});
%}


%% Copyright (C) 2016-2022 Colin B. Macdonald
%%
%% This file is part of OctSymPy.
%%
%% OctSymPy 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 software 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 software; see the file COPYING.
%% If not, see <http://www.gnu.org/licenses/>.

%% -*- texinfo -*-
%% @documentencoding UTF-8
%% @defmethod @@sym sinhint (@var{x})
%% Symbolic sinhint function.
%%
%% Example:
%% @example
%% @group
%% syms x
%% y = sinhint (x)
%%   @result{} y = (sym) Shi(x)
%% @end group
%% @end example
%%
%% Note: this file is autogenerated: if you want to edit it, you might
%% want to make changes to 'generate_functions.py' instead.
%%
%% @end defmethod

function y = sinhint(x)
  if (nargin ~= 1)
    print_usage ();
  end
  y = elementwise_op ('Shi', x);
end

%!error sinhint (sym(1), 2)
%!xtest
%! assert (isequaln (sinhint (sym(nan)), sym(nan)))

%!shared x, d
%! d = 1;
%! x = sym('1');

%!test
%! f1 = sinhint(x);
%! f2 = 1.057250875375728514572;
%! assert( abs(double(f1) - f2) < 1e-15 )

%!test
%! D = [d d; d d];
%! A = [x x; x x];
%! f1 = sinhint(A);
%! f2 = 1.057250875375728514572;
%! f2 = [f2 f2; f2 f2];
%! assert( all(all( abs(double(f1) - f2) < 1e-15 )))

%!test
%! % round trip
%! y = sym('y');
%! A = sinhint (d);
%! f = sinhint (y);
%! h = function_handle (f);
%! B = h (d);
%! assert (A, B, -eps)


% SIZE - size of memory mapped underlying array
%
% Author: Arnaud Delorme, SCCN, INC, UCSD, Nov. 2008

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

function [s varargout] = size(obj,dim)

    if obj.transposed
        if length(obj.dimensions) ~= 2 && length(obj.dimensions) ~= 3
            error('Transposed array must be 2 or 3 dims');
        end
        if length(obj.dimensions) == 2 tmpdimensions = [obj.dimensions(2) obj.dimensions(1)];
        else                           tmpdimensions = [obj.dimensions(3) obj.dimensions(1:end-1)];
        end
    else
        tmpdimensions = obj.dimensions;
    end

    s = tmpdimensions;

    if nargin > 1
        if dim >length(s)
            s = 1;
        else
            s = s(dim);
        end
    else
        if nargout > 1
            s = [s 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1];
            alls = s;
            s = s(1);
            for index = 1:max(nargout,1)-1
                varargout{index} = alls(index+1);
            end
        end
    end


function N = readNEIGH(filename)
  % READNEIGH  Read tetrahedral neighbor information from a .neigh file (as
  % produced by tetgen)
  %
  % N = readNEIGH(filename)
  %
  % Inputs:
  %   filename  path to .neigh file
  % Outputs:
  %   N  #simplices by #size-of-simplex neighborhood information (-1) indicates
  %     boundary. T(i,j) *should* indicate the neighbor to the jth face of the
  %     ith tet. *However* tetgen does not seem consistent. Consider
  %     post-processing with fixNEIGH.m
  %
  % See also: tt, tetgen, readNODE, readELE, fixNEIGH
  %

  fp = fopen(filename);
  line = fscanf(fp,' %[^\n]s');
  [header,count] = sscanf(line,'%d %d',2);
  if count~=2
    fclose(fp);
    error('Bad header');
  end

  % number of elements
  n = header(1);
  % size of an element
  size_e = header(2);

  parser = '%d';
  % append to parser enough to read all entries in element + 1 for index
  parser = [parser repmat(' %d',1,size_e+1)];
  N = fscanf(fp,parser,[size_e+1 n])';
  fclose(fp);

  % get rid of row indices and make one indexed
  N = N(:,2:end) + 1;
  N(~N) = -1;
end


function mrtrix_itkclass_wmmask(classfile, fname)

% Convert the white matter segmentation file in itkGray (mrVista) into
% binary white matter mask in mrTrix.
%
% In itkGray (mrVista), white matter is described as 3 and 4 in the matrix
% in nifti file. This code converts itkGray segmentation into binary white
% matter mask (white matter:1, others:0) in order to use it in fiber tractography.
%
% INPUT:
% classfile: A full path to nifti file storing segmentation information in itkGray format
% fname: The name of the output file (does not require filename extension)
%
% EXAMPLE:
% classfile = 't1_class.nii.gz';
% fname = 't1_class_binary';
% mrtrix_itkclass_wmmask(classfile, fname)
% (C) Hiromasa Takemura, CiNet/Stanford VISTA Team, 2015

if notDefined('fname')
    fname = 't1_class_binary';
end

% Set savefile name
niftiname = [fname '.nii.gz'];
mifname = [fname '.mif'];

% Load file
classseg = niftiRead(classfile);

% Make new nifti structure and set file name
nii = classseg;
nii.fname = niftiname;

%% Make binary white matter mask.
% In ITKGray format, white matter is described as 3 and 4. Here, we put
% 1 for all white matter voxels and 0 for all other voxels.

% Set zero for all voxels
nii.data = zeros(size(classseg.data));

% Set one for white matter voxels
nii.data(classseg.data == 3) = 1;
nii.data(classseg.data == 4) = 1;

% Save binary nifti file
niftiWrite(nii);

%% Convert nifti to .mif format
mrtrix_mrconvert(niftiname, mifname);


function medit_to_fem ( prefix )

%*****************************************************************************80
%
%% MAIN is the main program for TRIANGLE_TO_FEM.
%
%  Discussion:
%
%    MEDIT_TO_FEM converts mesh data from MEDIT to FEM format.
%
%  Usage:
%
%    medit_to_fem prefix
%
%    where 'prefix' is the common filename prefix:
%
%    * 'prefix'.mesh is the MEDIT mesh file.
%    * 'prefix'_nodes.txt will contain the node coordinates.
%    * 'prefix'_elements.txt will contain the element node connectivity.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    10 November 2014
%
%  Author:
%
%    John Burkardt
%
  timestamp ( );
  fprintf ( 1, '\n' );
  fprintf ( 1, 'MEDIT_TO_FEM\n' );
  fprintf ( 1, '  MATLAB version:\n' );
  fprintf ( 1, '  Read a mesh description created by MEDIT\n' );
  fprintf ( 1, '  * "prefix".mesh.\n' );
  fprintf ( 1, '  Write two simple FEM files listing nodes and elements.\n' );
  fprintf ( 1, '  * "prefix"_nodes.txt, node coordinates.\n' );
  fprintf ( 1, '  * "prefix"_elements.txt, element connectivity.\n' );
%
%  Get the filename prefix.
%
  if ( nargin < 1 )

    prefix = input ( 'Enter the filename prefix:  ' );

  end
%
%  Create the filenames.
%
  medit_filename = strcat ( prefix, '.mesh' );
  fem_node_filename = strcat ( prefix, '_nodes.txt' );
  fem_element_filename = strcat ( prefix, '_elements.txt' );
%
%  Read MEDIT sizes.
%
  [ dim, vertices, edges, triangles, quadrilaterals, tetrahedrons, ...
    hexahedrons ] = mesh_size_read ( medit_filename );
%
%  Report sizes.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Number of dimensions = %d\n', dim );
  fprintf ( 1, '  Number of vertices = %d\n', vertices );
  fprintf ( 1, '  Number of edges = %d\n', edges );
  fprintf ( 1, '  Number of triangles = %d\n', triangles );
  fprintf ( 1, '  Number of quadrilaterals = %d\n', quadrilaterals );
  fprintf ( 1, '  Number of tetrahedrons = %d\n', tetrahedrons );
  fprintf ( 1, '  Number of hexahedrons = %d\n', hexahedrons );
%
%  Read MEDIT data.
%
  [ vertex_coordinate, vertex_label, edge_vertex, edge_label, ...
    triangle_vertex, triangle_label, quadrilateral_vertex, ...
    quadrilateral_label, tetrahedron_vertex, tetrahedron_label, ...
    hexahedron_vertex, hexahedron_label ] = mesh_data_read ( medit_filename, ...
    dim, vertices, edges, triangles, quadrilaterals, tetrahedrons, ...
    hexahedrons );
%
%  Choose the FEM data.
%
%  We need to assume that there is only one element type.
%  If there are elements of multiple dimension, take the highest.
%
  m = dim;
  node_num = vertices;
  r8mat_write ( fem_node_filename, dim, vertices, vertex_coordinate );

  fprintf ( 1, '\n' );
  fprintf ( 1, '  Created node coordinate file "%s"\n', fem_node_filename );

  if ( 0 < hexahedrons && dim == 3 )

    element_order = 8;
    element_num = hexahedrons;
    i4mat_write ( fem_element_filename, element_order, element_num, ...
      hexahedron_vertex );

  elseif ( 0 < tetrahedrons && dim == 3 )

    element_order = 4;
    element_num = tetrahedrons;
    i4mat_write ( fem_element_filename, element_order, element_num, ...
      tetrahedron_vertex );

  elseif ( 0 < quadrilaterals && dim == 2 )

    element_order = 4;
    element_num = quadrilaterals;
    i4mat_write ( fem_element_filename, element_order, element_num, ...
      quadrilateral_vertex );

  elseif ( 0 < triangles && dim == 2 )

    element_order = 3;
    element_num = triangles;
    i4mat_write ( fem_element_filename, element_order, element_num, ...
      triangle_vertex );

  else

    fprintf ( 1, '\n' );
    fprintf ( 1, 'MEDIT_TO_FEM - Fatal error!\n' );
    fprintf ( 1, '  Unexpected combination of spatial dimension\n' );
    fprintf ( 1, '  and number of nonzero objects.\n' );
    error ( 'MEDIT_TO_FEM - Fatal error!' );

  end

  fprintf ( 1, '  Created element connectivity file "%s"\n', ...
    fem_element_filename );
%
%  Terminate.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'MEDIT_TO_FEM:\n' );
  fprintf ( 1, '  Normal end of execution.\n' );
  fprintf ( 1, '\n' );
  timestamp ( );

  return
end
function c = ch_cap ( c )

%*****************************************************************************80
%
%% CH_CAP capitalizes a single character.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 February 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character C, the character to capitalize.
%
%    Output, character C, the capitalized character.
%
  if ( 'a' <= c && c <= 'z' )
    c = c + 'A' - 'a';
  end

  return
end
function truefalse = ch_eqi ( c1, c2 )

%*****************************************************************************80
%
%% CH_EQI is a case insensitive comparison of two characters for equality.
%
%  Example:
%
%    CH_EQI ( 'A', 'a' ) is TRUE.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    28 July 2000
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character C1, C2, the characters to compare.
%
%    Output, logical TRUEFALSE, is TRUE (1) if the characters are equal.
%
  FALSE = 0;
  TRUE = 1;

  if ( ch_cap ( c1 ) == ch_cap ( c2 ) )
    truefalse = TRUE;
  else
    truefalse = FALSE;
  end

  return
end
function i4mat_write ( output_filename, m, n, table )

%*****************************************************************************80
%
%% I4MAT_WRITE writes an I4MAT file.
%
%  Discussion:
%
%    An I4MAT is an array of I4's.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    26 June 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string OUTPUT_FILENAME, the output filename.
%
%    Input, integer M, the spatial dimension.
%
%    Input, integer N, the number of points.
%
%    Input, integer TABLE(M,N), the points.
%

%
%  Open the file.
%
  output_unit = fopen ( output_filename, 'wt' );

  if ( output_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'I4MAT_WRITE - Error!\n' );
    fprintf ( 1, '  Could not open the output file.\n' );
    error ( 'I4MAT_WRITE - Error!' );
  end
%
%  Write the data.
%
  for j = 1 : n
    for i = 1 : m
      fprintf ( output_unit, '  %d', round ( table(i,j) ) );
    end
    fprintf ( output_unit, '\n' );
  end
%
%  Close the file.
%
  fclose ( output_unit );

  return
end
function [ vertex_coordinate, vertex_label, edge_vertex, edge_label, ...
  triangle_vertex, triangle_label, quadrilateral_vertex, ...
  quadrilateral_label, tetrahedron_vertex, tetrahedron_label, ...
  hexahedron_vertex, hexahedron_label ] = mesh_data_read ( filename, dim, ...
  vertices, edges, triangles, quadrilaterals, tetrahedrons, hexahedrons )

%*****************************************************************************80
%
%% MESH_READ reads a MESH file defining a mesh and returns the data.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 November 2014
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Pascal Frey,
%    MEDIT: An interactive mesh visualization software,
%    Technical Report RT-0253,
%    Institut National de Recherche en Informatique et en Automatique,
%    03 December 2001.
%
%  Parameters:
%
%    Input, string FILENAME, the name of the MESH file.
%
%    Input, integer DIM, the spatial dimension, which should be 2 or 3.
%
%    Input, integer VERTICES, the number of vertices.
%
%    Input, integer EDGES, the number of edges (may be 0).
%
%    Input, integer TRIANGLES, the number of triangles (may be 0).
%
%    Input, integer QUADRILATERALS, the number of quadrilaterals (may be 0).
%
%    Input, integer TETRAHEDRONS, the number of tetrahedrons (may be 0).
%
%    Input, integer HEXAHEDRONS, the number of hexahedrons (may be 0).
%
%    Output, real VERTEX_COORDINATE(DIM,VERTICES), the coordinates
%    of each vertex.
%
%    Output, integer VERTEX_LABEL(VERTICES), a label for each vertex.
%
%    Output, integer EDGE_VERTEX(2,EDGES), the vertices that form each edge.
%
%    Output, integer EDGE_LABEL(EDGES), a label for each edge.
%
%    Output, integer TRIANGLE_VERTEX(3,TRIANGLES), the vertices that form
%    each triangle.
%
%    Output, integer TRIANGLE_LABEL(TRIANGLES), a label for each triangle.
%
%    Output, integer QUADRILATERAL_VERTEX(4,QUADRILATERALS), the vertices that
%    form each quadrilateral.
%
%    Output, integer QUADRILATERAL_LABEL(QUADRILATERALS), a label for
%    each quadrilateral.
%
%    Output, integer TETRAHEDRON_VERTEX(4,TETRAHEDRONS), the vertices that
%    form each tetrahedron.
%
%    Output, integer TETRAHEDRON_LABEL(TETRAHEDRONS), a label for
%    each tetrahedron.
%
%    Output, integer HEXAHEDRON_VERTEX(8,HEXAHEDRONS), the vertices that form
%    each hexahedron.
%
%    Output, integer HEXAHEDRON_LABEL(HEXAHEDRONS), a label for each hexahedron.
%

%
%  Initialize everything to nothing.
%
  vertex_coordinate = [];
  vertex_label = [];
  edge_vertex = [];
  edge_label = [];
  triangle_vertex = [];
  triangle_label = [];
  quadrilateral_vertex = [];
  quadrilateral_label = [];
  tetrahedron_vertex = [];
  tetrahedron_label = [];
  hexahedron_vertex = [];
  hexahedron_label = [];
%
%  Open the file.
%
  unit = fopen ( filename, 'rt' );

  if ( unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'MESH_DATA_READ - Error!\n' );
    fprintf ( 1, '  Could not open the input file "%s".\n', filename );
    error ( 'MESH_DATA_READ - Error!' );
    return
  end
%
%  Read lines til you get alphanumerics and determine a "mode"
%
  line_num = 0;
  keyword = 'NONE';

  while ( 1 )

    text = fgetl ( unit );

    if ( text == -1 )
      break
    end

    line_num = line_num + 1;

    if ( s_len_trim ( text ) == 0 )
      keyword = 'NONE';
      continue
    end

    if ( text(1) == '#' )
      continue
    end
%
%  Remove initial blanks.
%

%
%  Expecting a keyword.
%
        if ( s_eqi ( text, 'CORNERS' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'DIMENSION' ) )

      keyword = 'DIMENSION';

    elseif ( s_eqi ( text, 'EDGES' ) )

      keyword = 'EDGES';

    elseif ( s_eqi ( text, 'END' ) )

      fprintf ( 1, '\n' );
      fprintf ( 1, '  END statement encountered.\n' );
      break

    elseif ( s_eqi ( text, 'HEXAHEDRA' ) || s_eqi ( text, 'HEXAHEDRONS' ) )

      keyword = 'HEXAHEDRONS';

    elseif ( s_begin ( text, 'MESHVERSIONFORMATTED' ) )

    elseif ( s_eqi ( text, 'NORMALATQUADRILATERALVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'NORMALATTRIANGLEVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'NORMALATVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'NORMALS' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'QUADRILATERALS' ) )

      keyword = 'QUADRILATERALS';

    elseif ( s_eqi ( text, 'REQUIREDEDGES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'REQUIREDVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'RIDGES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'TANGENTATEDGES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'TANGENTS' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'TETRAHEDRA' ) || s_eqi ( text, 'TETRAHEDRONS' ) )

      keyword = 'TETRAHEDRONS';

    elseif ( s_eqi ( text, 'TRIANGLES' ) )

      keyword = 'TRIANGLES';

    elseif ( s_eqi ( text, 'VERTICES' ) )

      keyword = 'VERTICES';
%
%  Presumably, numeric data to be processed by keyword.
%
    elseif ( s_eqi ( keyword, 'DIMENSION' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       dim = value;
      end

      keyword = 'NONE';

    elseif ( s_eqi ( keyword, 'EDGES' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       edges = value;
      end

      keyword = 'EDGE_VERTEX';
      edge = 0;
      edge_vertex = zeros ( 2, edges );
      edge_label = zeros ( 1, edges );

    elseif ( s_eqi ( keyword, 'EDGE_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d' );

      edge = edge + 1;
      edge_vertex(1:2,edge) = value(1:2);
      edge_label(edge) = value(3);

    elseif ( s_eqi ( keyword, 'HEXAHEDRONS' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       hexahedrons = value;
      end

      keyword = 'HEXAHEDRON_VERTEX';
      hexahedron = 0;
      hexahedron_vertex = zeros ( 8, hexahedrons );
      hexahedron_label = zeros ( 1, hexahedrons );

    elseif ( s_eqi ( keyword, 'HEXAHEDRON_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d  %d  %d  %d  %d  %d' );

      hexahedron = hexahedron + 1;
      hexahedron_vertex(1:8,hexahedron) = value(1:8);
      hexahedron_label(hexahedron) = value(9);

    elseif ( s_eqi ( keyword, 'QUADRILATERALS' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       quadrilaterals = value;
      end

      keyword = 'QUADRILATERAL_VERTEX';
      quadrilateral = 0;
      quadrilateral_vertex = zeros ( 4, quadrilateral );
      quadrilateral_label = zeros ( 1, quadrilateral );

    elseif ( s_eqi ( keyword, 'QUADRILATERAL_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d  %d' );

      quadrilateral = quadrilateral + 1;
      quadrilateral_vertex(1:4,quadrilateral) = value(1:4);
      quadrilateral_label(quadrilateral) = value(5);

    elseif ( s_eqi ( keyword, 'TETRAHEDRONS' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       tetrahedrons = value;
      end

      keyword = 'TETRAHEDRON_VERTEX';
      tetrahedron = 0;
      tetrahedron_vertex = zeros ( 4, tetrahedron );
      tetrahedron_label = zeros ( 1, tetrahedron );

    elseif ( s_eqi ( keyword, 'TETRAHEDRON_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d  %d' );

      tetrahedron = tetrahedron + 1;
      tetrahedron_vertex(1:4,tetrahedron) = value(1:4);
      tetrahedron_label(tetrahedron) = value(5);

    elseif ( s_eqi ( keyword, 'TRIANGLES' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       triangles = value;
      end

      keyword = 'TRIANGLE_VERTEX';
      triangle = 0;
      triangle_vertex = zeros ( 3, triangle );
      triangle_label = zeros ( 1, triangle );

    elseif ( s_eqi ( keyword, 'TRIANGLE_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d' );

      triangle = triangle + 1;
      triangle_vertex(1:3,triangle) = value(1:3);
      triangle_label(triangle) = value(4);

    elseif ( s_eqi ( keyword, 'VERTICES' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       vertices = value;
      end

      keyword = 'VERTEX_COORDINATE';
      vertex = 0;
      vertex_coordinate = zeros ( dim, vertices );
      vertex_label = zeros ( 1, vertices );

    elseif ( s_eqi ( keyword, 'VERTEX_COORDINATE' ) )

      if ( dim == 2 )
        [ value, count ] = sscanf ( text, '%f  %f  %d' );
      elseif ( dim == 3 )
        [ value, count ] = sscanf ( text, '%f  %f  %f  %d' );
      end

      vertex = vertex + 1;
      vertex_coordinate(1:dim,vertex) = value(1:dim);
      vertex_label(vertex) = value(dim+1);

    elseif ( s_eqi ( keyword, 'SKIP' ) )

    else

      fprintf ( 1, '\n' );
      fprintf ( 1, 'MESH_DATA_READ - Fatal error!\n' );
      fprintf ( 1, '  Could not find keyword while reading line %d:\n', line_num );
      fprintf ( 1, '"%s"\n', text );
      error ( 'MESH_DATA_READ - Fatal error!\n' );

    end
  end
%
%  Close the file.
%
  fclose ( unit );

  fprintf ( 1, '\n' );
  fprintf ( 1, '  Read %d lines from "%s".\n', line_num, filename );

  return
end
function [ dim, vertices, edges, triangles, quadrilaterals, tetrahedrons, ...
  hexahedrons ] = mesh_size_read ( filename )

%*****************************************************************************80
%
%% MESH_SIZE_READ reads a MESH file defining a mesh and returns sizes only.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    03 December 2010
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Pascal Frey,
%    MEDIT: An interactive mesh visualization software,
%    Technical Report RT-0253,
%    Institut National de Recherche en Informatique et en Automatique,
%    03 December 2001.
%
%  Parameters:
%
%    Input, string FILENAME, the name of the MESH file.
%
%    Output, integer DIM, the spatial dimension, which should be 2 or 3.
%
%    Output, integer VERTICES, the number of vertices.
%
%    Output, integer EDGES, the number of edges (may be 0).
%
%    Output, integer TRIANGLES, the number of triangles (may be 0).
%
%    Output, integer QUADRILATERALS, the number of quadrilaterals (may be 0).
%
%    Output, integer TETRAHEDRONS, the number of tetrahedrons (may be 0).
%
%    Output, integer HEXAHEDRONS, the number of hexahedrons (may be 0).
%

%
%  Initialize everything to nothing.
%
  dim = 0;
  vertices = 0;
  edges = 0;
  triangles = 0;
  quadrilaterals = 0;
  tetrahedrons = 0;
  hexahedrons = 0;
%
%  Open the file.
%
  unit = fopen ( filename, 'rt' );

  if ( unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'MESH_SIZE_READ - Error!\n' );
    fprintf ( 1, '  Could not open the input file "%s".\n', filename );
    error ( 'MESH_SIZE_READ - Error!' );
    return
  end
%
%  Read lines til you get alphanumerics and determine a "mode"
%
  line_num = 0;
  keyword = 'NONE';

  while ( 1 )

    text = fgetl ( unit );

    if ( text == -1 )
      break
    end

    line_num = line_num + 1;

    if ( s_len_trim ( text ) == 0 )
      keyword = 'NONE';
      continue
    end

    if ( text(1) == '#' )
      continue
    end
%
%  Remove initial blanks.
%

%
%  Expecting a keyword.
%
        if ( s_eqi ( text, 'CORNERS' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'DIMENSION' ) )

      keyword = 'DIMENSION';

    elseif ( s_eqi ( text, 'EDGES' ) )

      keyword = 'EDGES';

    elseif ( s_eqi ( text, 'END' ) )

      fprintf ( 1, '\n' );
      fprintf ( 1, '  END statement encountered.\n' );
      break

    elseif ( s_eqi ( text, 'HEXAHEDRA' ) || s_eqi ( text, 'HEXAHEDRONS' ) )

      keyword = 'HEXAHEDRONS';

    elseif ( s_begin ( text, 'MESHVERSIONFORMATTED' ) )

    elseif ( s_eqi ( text, 'NORMALATQUADRILATERALVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'NORMALATTRIANGLEVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'NORMALATVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'NORMALS' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'QUADRILATERALS' ) )

      keyword = 'QUADRILATERALS';

    elseif ( s_eqi ( text, 'REQUIREDEDGES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'REQUIREDVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'RIDGES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'TANGENTATEDGES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'TANGENTS' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'TETRAHEDRA' ) || s_eqi ( text, 'TETRAHEDRONS' ) )

      keyword = 'TETRAHEDRONS';

    elseif ( s_eqi ( text, 'TRIANGLES' ) )

      keyword = 'TRIANGLES';

    elseif ( s_eqi ( text, 'VERTICES' ) )

      keyword = 'VERTICES';
%
%  Presumably, numeric data to be processed by keyword.
%
    elseif ( s_eqi ( keyword, 'DIMENSION' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
        dim = value;
      end

      keyword = 'NONE';

    elseif ( s_eqi ( keyword, 'EDGES' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
        edges = value;
      end

      keyword = 'EDGE_VERTEX';

    elseif ( s_eqi ( keyword, 'EDGE_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d' );

    elseif ( s_eqi ( keyword, 'HEXAHEDRONS' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
        hexahedrons = value;
      end

      keyword = 'HEXAHEDRON_VERTEX';

    elseif ( s_eqi ( keyword, 'HEXAHEDRON_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d  %d  %d  %d  %d  %d' );

    elseif ( s_eqi ( keyword, 'QUADRILATERALS' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
        quadrilaterals = value;
      end

      keyword = 'QUADRILATERAL_VERTEX';

    elseif ( s_eqi ( keyword, 'QUADRILATERAL_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d  %d' );

    elseif ( s_eqi ( keyword, 'TETRAHEDRONS' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
        tetrahedrons = value;
      end

      keyword = 'TETRAHEDRON_VERTEX';

    elseif ( s_eqi ( keyword, 'TETRAHEDRON_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d  %d' );

    elseif ( s_eqi ( keyword, 'TRIANGLES' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
        triangles = value;
      end

      keyword = 'TRIANGLE_VERTEX';

    elseif ( s_eqi ( keyword, 'TRIANGLE_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d' );

    elseif ( s_eqi ( keyword, 'VERTICES' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
        vertices = value;
      end

      keyword = 'VERTEX_COORDINATE';

    elseif ( s_eqi ( keyword, 'VERTEX_COORDINATE' ) )

      if ( dim == 2 )
        [ value, count ] = sscanf ( text, '%f  %f  %d' );
      elseif ( dim == 3 )
        [ value, count ] = sscanf ( text, '%f  %f  %f  %d' );
      end

    elseif ( s_eqi ( keyword, 'SKIP' ) )

    else

      fprintf ( 1, '\n' );
      fprintf ( 1, 'MESH_SIZE_READ - Fatal error!\n' );
      fprintf ( 1, '  Could not find keyword while reading line %d:\n', line_num );
      fprintf ( 1, '"%s"\n', text );
      error ( 'MESH_SIZE_READ - Fatal error!\n' );

    end
  end
%
%  Close the file.
%
  fclose ( unit );

  fprintf ( 1, '\n' );
  fprintf ( 1, '  Read %d lines from "%s".\n', line_num, filename );

  return
end
function r8mat_write ( output_filename, m, n, table )

%*****************************************************************************80
%
%% R8MAT_WRITE writes an R8MAT file.
%
%  Discussion:
%
%    An R8MAT is an array of R8's.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 February 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string OUTPUT_FILENAME, the output filename.
%
%    Input, integer M, the spatial dimension.
%
%    Input, integer N, the number of points.
%
%    Input, real TABLE(M,N), the points.
%

%
%  Open the file.
%
  output_unit = fopen ( output_filename, 'wt' );

  if ( output_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R8MAT_WRITE - Error!\n' );
    fprintf ( 1, '  Could not open the output file.\n' );
    error ( 'R8MAT_WRITE - Error!' );
  end
%
%  Write the data.
%
%  Alternative print statements include:
%
%     fprintf ( output_unit, '  %24.16e', table(i,j) );
%     fprintf ( output_unit, '  %14.6e', table(i,j) );
%
  for j = 1 : n
    for i = 1 : m
      fprintf ( output_unit, '  %g', table(i,j) );
    end
    fprintf ( output_unit, '\n' );
  end
%
%  Close the file.
%
  fclose ( output_unit );

  return
end
function value = s_begin ( s1, s2 )

%*****************************************************************************80
%
%% S_BEGIN is TRUE if one string matches the beginning of the other.
%
%  Discussion:
%
%    The strings are compared, ignoring blanks and capitalization.
%
%  Example:
%
%     S1              S2      S_BEGIN
%
%    'Bob'          'BOB'     TRUE
%    '  B  o b '    ' bo b'   TRUE
%    'Bob'          'Bobby'   TRUE
%    'Bobo'         'Bobb'    FALSE
%    ' '            'Bob'     FALSE    (Do not allow a blank to match
%                                       anything but another blank string.)
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    30 January 2006
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character S1(*), S2(*), the strings to be compared.
%
%    Output, logical S_BEGIN, is TRUE if the strings match up to
%    the end of the shorter string, ignoring case.
%
  len1 = s_len_trim ( s1 );
  len2 = s_len_trim ( s2 );
%
%  If either string is blank, then both must be blank to match.
%  Otherwise, a blank string matches anything, which is not
%  what most people want.
%
  if ( len1 == 0 || len2 == 0 )

    if ( len1 == 0 && len2 == 0 )
      value = 1;
    else
      value = 0;
    end

    return

  end

  i1 = 0;
  i2 = 0;
%
%  Find the next nonblank in S1.
%
  while ( 1 )

    while ( 1 )

      i1 = i1 + 1;

      if ( len1 < i1 )
        value = 1;
        return
      end

      if ( s1(i1) ~= ' ' )
        break
      end

    end
%
%  Find the next nonblank in S2.
%
    while ( 1 )

      i2 = i2 + 1;

      if ( len2 < i2 )
        value = 1;
        return
      end

      if ( s2(i2) ~= ' ' )
        break
      end

    end
%
%  If the characters match, get the next pair.
%
    if ( ~ch_eqi ( s1(i1), s2(i2) ) )
      break
    end

  end

  value = 0;

  return
end
function value = s_eqi ( s1, s2 )

%*****************************************************************************80
%
%% S_EQI is a case insensitive comparison of two strings for equality.
%
%  Example:
%
%    S_EQI ( 'Anjana', 'ANJANA' ) is TRUE.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    30 April 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string S1, S2, the strings to compare.
%
%    Output, logical VALUE, is TRUE if the strings are equal.
%
  len1 = length ( s1 );
  len2 = length ( s2 );
  lenc = min ( len1, len2 );

  for i = 1 : lenc

    c1 = ch_cap ( s1(i) );
    c2 = ch_cap ( s2(i) );

    if ( c1 ~= c2 )
      value = 0;
      return
    end

  end

  for i = lenc + 1 : len1
    if ( s1(i) ~= ' ' )
      value = 0;
      return
    end
  end

  for i = lenc + 1 : len2
    if ( s2(i) ~= ' ' )
      value = 0;
      return
    end
  end

  value = 1;

  return
end
function len = s_len_trim ( s )

%*****************************************************************************80
%
% S_LEN_TRIM returns the length of a character string to the last nonblank.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 June 2003
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string S, the string to be measured.
%
%    Output, integer LENGTH, the length of the string up to the last nonblank.
%
  len = length ( s );

  while ( 0 < len )
    if ( s(len) ~= ' ' )
      return
    end
    len = len - 1;
  end

  return
end
function timestamp ( )

%*****************************************************************************80
%
%% TIMESTAMP prints the current YMDHMS date as a timestamp.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 February 2003
%
%  Author:
%
%    John Burkardt
%
  t = now;
  c = datevec ( t );
  s = datestr ( c, 0 );
  fprintf ( 1, '%s\n', s );

  return
end


function [centers, labels, info] = slkmeansex(X, n, estfunctor, clsfunctor, varargin)
%SLKMEANSEX Performs Generalized K-means
%
% $ Syntax $
%   - [centers, labels] = slkmeansex(X, n, estfunctor, clsfunctor, ...)
%   - [centers, labels, info] = slkmeansex(X, n, estfunctor, clsfunctor, ...)
%
% $ Arguments $
%   - X:            the samples to be clustered
%   - n:            the number of samples
%   - estfunctor:   the functor to estimate means(centers), as follows:
%                   centers = estfunc(centers, X, K, weights, labels, ...)
%                   when input centers is empty, it performs initial
%                   estimation, otherwise, it performs updating.
%                   In addition, it should ignore the samples with
%                   labels being zeros or negative numbers.
%   - clsfunctor:   the functor to classify samples
%                   labels = clsfunc(centers, X, n, ...)
%                   it should produce 1 x n row vector.
%   - centers:      the clustered centers
%   - labels:       the labels indicating which sample belong to which center
%                   a 1 x n row vector.
%   - info:         the information on iteration process
%
% $ Description $
%   - [centers, labels] = slkmeansex(X, n, estfunctor, clsfunctor, ...)
%     is a generalized version of K-means. It actually implements an
%     iterative process to estimate centers from clustered samples and
%     re-clustered the samples according to centers.
%     You can specify the following properties:
%       - 'K':              the number of initial number of clusters
%                           (default = 3)
%       - 'init_centers':   the initial centers.
%       - 'maxiter':        the maximum number of iterations
%                           (default = 100);
%       - 'annthres':       the threshold of annealing
%                           when the sum of sample weights for a center
%                           is below annthres * the total weight, the
%                           center will be discarded. (default = 0)
%       - 'annfunc':        the function to discard a set of centers
%                           centers = annfunc(centers, inds_discard);
%       - 'weights':        the weights of the samples (default = [])
%       - 'verbose':        whether to show progress information
%                           (default = true)
%
% $ Remarks $
%   - The X and centers can be in any form that conform to the specified
%     functors.
%
%   - If init_centers is specified, K should be exactly the number of
%     initial centers.
%
%   - If annthres is 0, then no centers will be discarded even some centers
%     have no support samples in the process. The estfunctor should keep
%     those centers unchanged.
%
% $ History $
%   - Created by Dahua Lin, on Aug 28, 2006
%   - Modified by Dahua Lin, on Aug 30, 2006
%       - utilize slevalfunctor and slsharedisp
%   - Modified by Dahua Lin, on Aug 31, 2006
%       - based on slreevallearn
%

%% parse and verify input arguments

if nargin < 4
    raise_lackinput('slkmeansex', 4);
end

opts.K = 3;
opts.init_centers = [];
opts.maxiter = 100;
opts.annthres = 0;
opts.annfunc = [];
opts.weights = [];
opts.verbose = true;
opts = slparseprops(opts, varargin{:});

if opts.K > n
    error('sltoolbox:rterror', ...
        'The initial K is larger than the number of samples');
end

if opts.annthres > 0
    if isempty(opts.annfunc)
        error('sltoolbox:invalidarg', ...
            'You should specify annfunc when annthres > 0');
    end
end

w = opts.weights;
if ~isempty(w)
    if ~isequal(w, [1 n])
        error('sltoolbox:sizmismatch', ...
            'The weights should be a 1 x n row vector');
    end
end

%% Initialization

slsharedisp_attach('slkmeansex', 'show', opts.verbose);

slsharedisp('Intialize K-Means');

if isempty(opts.init_centers)
    initcinds = randsample(n, opts.K);
    labels = zeros(1, n);
    labels(initcinds) = 1:opts.K;

    K = opts.K;
    centers = slevalfunctor(estfunctor, [], X, K, w, labels);
else
    K = opts.K;
    centers = opts.init_centers;
end

slsharedisp_incindent;
slsharedisp('initial K = %d', K);
slsharedisp_decindent;

labels = slevalfunctor(clsfunctor, centers, X, n);

%% Updating

slsharedisp('Update K-Means');
slsharedisp_incindent;

km_estfunctor = {@kmeansex_est, estfunctor, opts};
km_evalfunctor = {@kmeansex_eval, clsfunctor};
km_cmpfunctor = {@kmeansex_cmp};

models = {centers, K};
data = {X, n, w};
[models, labels, info] = slreevallearn(models, labels, data, ...
    km_estfunctor, km_evalfunctor, km_cmpfunctor, ...
    'iter', {'maxiter', opts.maxiter, 'titlebreak', false}, 'isrecorded', false);

centers = models{1};

slsharedisp_decindent;
slsharedisp_detach;

%% Core functions

% models = {centers, K}
% data = {X, n, w}

function models = kmeansex_est(models, data, labels, estfunctor, opts)

X = data{1};
w = data{3};
centers = models{1};
K = models{2};

if ~isempty(centers) && opts.annthres > 0
    if isempty(w)
        w = ones(1, length(labels));
    end
    cw = sllabeledsum(w, labels, 1:K);
    wthres = opts.annthres * sum(cw) / K;
    if any(cw < wthres)
        inds_ann = find(cw < wthres);
        centers = feval(opts.annfunc, centers, inds_ann);
        K = K - length(inds_ann);

        models = {centers, K};
        return;
    end
end

centers = slevalfunctor(estfunctor, centers, X, K, w, labels);
models = {centers, K};

function labels = kmeansex_eval(models, data, labels, clsfunctor)

X = data{1};
n = data{2};
centers = models{1};

slignorevars(labels);

labels = slevalfunctor(clsfunctor, centers, X, n);

function isconverged = kmeansex_cmp(models_prev, models, labels_prev, labels)

K_prev = models_prev{2};
K = models{2};
n = length(labels);

slsharedisp_attach('kmeansex_cmp');

isconverged = false;
if K == K_prev
    nchanged = sum(labels ~= labels_prev);
    slsharedisp('K = %d: %d / %d changed', K, nchanged, n);

    if nchanged == 0
        isconverged = true;
    end
else
    slsharedisp('K = %d ==> %d', K_prev, K);
end

slsharedisp_detach();


function [ V VS ] = dicomreadvolume(zipfile)
%DICOMREADVOLUME   Read a zip-file with DICOM slices as volume.
%   [V VS] = DICOMREADVOLUME(FNAME) loads the 3-D volume data V from
%   the volume dicom slices in the zip-file FNAME.
%   The voxel size is returned by VS.
%
%   See also DICOMWRITEVOLUME, DICOMWRITE, DICOMREAD
%
%   Author: medical-image-processing.com

% extract the zip file to the temporary directory
fnames = unzip(zipfile, tempdir);

% number of files
N = numel(fnames);

if (N<1)
    error('Empty zip file');
end

% read first slice for determining slice properties
S = squeeze(dicomread(fnames{1}));
I1 = dicominfo(fnames{1});

% voxel size information
VS = [I1.PixelSpacing(:) ; I1.SliceThickness];

% slice size and datatype
sz = size(S);
tp = class(S);

% pre-allocate data
VT = zeros([sz N], tp);
V = VT;
POS = zeros(N,2);

% load each slice and its properties
for i=1:N
    VT(:,:,i) = squeeze(dicomread(fnames{i}));
    info = dicominfo(fnames{i});
    if isfield(info, 'ImagePositionPatient')
        POS(i,:) = [info.ImagePositionPatient(3) i];
    else
        POS(i,:) = [i i];
    end
    delete(fnames{i});
end

% resort the slices according to the image position
POS = sortrows(POS,1);
for i=1:N
   V(:,:,i) = VT(:,:,POS(i,2));
end


classdef ProjectorToP1discont < handle
    % OBJECTIU: encara no fa falta resoldre el sistema matricial. Simplement
    % fer switch depenent de la funcio d'entrada (P0 o P1) i trobar "a lo
    % cutre" la P1discont. Ja mes endavant fem el cas general. Ja hi ha
    % fragments de codi escampats que resolen P0toP1discont i P1toP1discont.

    properties (Access = public)

    end

    properties (Access = private)
        mesh
%         connec
%         quadOrder
    end

    properties (Access = private)
        quadrature
%         field
%         meshD
    end

    methods (Access = public)

        function obj = ProjectorToP1discont(cParams)
            obj.init(cParams);
%             obj.createDiscontinuousMesh();
            obj.createQuadrature();
%             obj.createField();
        end

%         function xFun = project(obj, x)
%             LHS = obj.computeLHS();
%             RHS = obj.computeRHS(x);
%         end

        function xFun = project(obj,cParams) % THIS WILL BE DELETED ONCE RHS IS OK
            origin = cParams.origin;
            x      = cParams.x;
            connec = obj.mesh.connec;
            switch origin
                case {'P0'}
                    dim      = 1;
                    ndim  = size(x.fValues, 1);
                    nnodeElem = size(connec,2);
                    fEl = squeeze(x.fValues(dim,:,:));
                    fRepeated = zeros(ndim, size(fEl,1), nnodeElem);
                    for idim = 1:ndim
                        fEl = squeeze(x.fValues(idim,:,:));
                        for iNode = 1:nnodeElem
                            fRepeated(idim, :,iNode) = fEl;
                        end
                    end
                    fD = permute(fRepeated, [1 3 2]);
                    fD = fD(1,:,:);
                    s.fValues = fD;
                    s.mesh    = obj.mesh;
                    xFun      = P1DiscontinuousFunction(s);
                case {'P1'}
                    f = x.fValues;
                    nNode  = size(connec,2);
                    nDime  = size(f,2);
                    nodes = reshape(connec',1,[]);
                    fe = f(nodes,:)';
                    fNodeElem = reshape(fe,nDime,nNode,[]);

                    s.fValues = fNodeElem;
                    s.mesh    = obj.mesh;
                    xFun = P1DiscontinuousFunction(s);
            end
        end

    end

    methods (Access = private)

        function init(obj, cParams)
            obj.mesh   = cParams.mesh;
        end

%         function createDiscontinuousMesh(obj)
%             obj.meshD = obj.mesh.createDiscontinuousMesh();
%         end

        function q = createQuadrature(obj)
            quadOrder = 'LINEAR';
            q = Quadrature.set(obj.mesh.type);
            q.computeQuadrature(quadOrder);
            obj.quadrature = q;
        end

%         function createField(obj)
%             s.mesh               = obj.meshD;
%             s.ndimf              = 1;
%             s.interpolationOrder = 'LINEAR';
%             s.quadratureOrder    = 'QUADRATIC';
%             obj.field = Field(s);
%         end
%
%         function LHS = computeLHS(obj)
%             s.type  = 'MassMatrix';
%             s.mesh  = obj.meshD;
%             s.field = obj.field;
%             lhs = LHSintegrator.create(s);
%             LHS = lhs.compute();
%         end
%
%         function RHS = computeRHS(obj, fun)
%             fDisc = fun.computeDiscontinuousField();
%             fVals = fDisc.fValues;
%             dV = obj.mesh.computeDvolume(obj.quadrature);
%             xV = obj.quadrature.posgp;
%             nGaus  = obj.quadrature.ngaus;
%             nF     = size(fVals,1);
%             nElem  = size(obj.mesh.connec,1);
%             rhs = zeros(nElem,nF);
%
%             % Separate in two loops
%             for igaus = 1:nGaus
%                 xGaus = xV(:,igaus);
%                 fGaus = fDisc.evaluate(xGaus);
%                 dVg(:,1) = dV(igaus,:);
%                 for iF = 1:nF
%                     fGausF = squeeze(fGaus(iF,:,:));
%                     Ni = 1;
%                     int = Ni*fGausF.*dVg;
%                     rhs(:,iF) = rhs(:,iF) + int;
%                 end
%             end
%         end

    end
end


function F = retrieveR(level, o, H, L)
%------------------------------------------------------------------------------
%
% This function extracts a two-dimensional gridfunction F from H.
% F is supposed to be uniquely determined by the integer level and
% character o describing its type. F is defined on a rectangular
% domain.
% This function is a two-dimensional lifting scheme utility.
%
% F = 2D gridfunction of coefficients extracted from H.
%
% level = integer designated as the level of F.
%
% o = character, should be either 'a' or 'd',
%     describing the type of F:
%     'a' relates to approximation (coefficients) and
%     'd' relates to detail (coefficients)
%
% L = 2D integer array of bookkeeping, see function storeR.
%
% H = 1D array that functions as storage (heap). The coefficients of F
%     are extracted from H as a result of calling retrieveR.
%
% See also: storeR, retrieveQ
%
% Design and implementation by:
% Dr. Paul M. de Zeeuw <Paul.de.Zeeuw@cwi.nl>  http://homepages.cwi.nl/~pauldz/
% Last Revision: May 5, 2002.
% (c) 1999-2002 Stichting CWI, Amsterdam.
%------------------------------------------------------------------------------
if isempty(L)
  if ~isempty(H)
    disp([' retrieveR - ERROR at type ' o ' with level ' num2str(level)]);
    error(' retrieveR - books empty but heap is not ')
  else
    F = [];
  end
else
  if isempty(H)
    disp([' retrieveR - ERROR at type ' o ' with level ' num2str(level)]);
    error(' retrieveR - books not empty but heap is ')
  else
    [nL, mL] = size(L);
    if mL ~= 6
      disp([' retrieveR - ERROR at type ' o ' level ' num2str(level)]);
      error(' retrieveR - books do not fit ')
    else
      jL = -1;
      j = 1;
      foundit = 0;
      while j <= nL && ~foundit
         if L(j, 1) == level
           switch o
               case 'a' , if L(j, 3) == 0 && L(j, 2) == 0
                            jL = j;
                            foundit = 1;
                          end
               case 'd' , if L(j, 3) == 1 && L(j, 2) == 0
                            jL = j;
                            foundit = 1;
                          end
               otherwise
               disp([' retrieveR - ERROR at type ' o ' level ' num2str(level)]);
               error(' retrieveR - unknown type of coefficients ')
           end
         end
         j = j + 1;
      end
      if jL == -1
        disp([' retrieveR - ERROR at type ' o ' level ' num2str(level)]);
        error(' retrieveR - no such coefficients in heap')
      else
        nF = L(jL, 4);
        if nF < 1
          disp([' retrieveR - ERROR at type ' o ' level ' num2str(level)]);
          error(' retrieveR - unvalid 1st dimension of target')
        end
        mF = L(jL, 5);
        if mF < 1
          disp([' retrieveR - ERROR at type ' o ' level ' num2str(level)]);
          error(' retrieveR - unvalid 2nd dimension of target')
        end
        heaptr = L(jL, 6);
        if heaptr < 2
          disp([' retrieveR - ERROR at type ' o ' level ' num2str(level)]);
          error(' retrieveR - bookkeeping error ')
        else
          [nH, mH] = size(H);
          if mH ~=1
            disp([' retrieveR - ERROR at type ' o ' level ' num2str(level)]);
            error(' retrieveR - heap should be column vector')
          else
            beginptr = heaptr-nF*mF;
            if heaptr-1 > nH
              disp([' retrieveR - ERROR at type ' o ' level ' num2str(level)]);
              error(' retrieveR - heap not that large')
            elseif beginptr < 1
              disp([' retrieveR - ERROR at type ' o ' level ' num2str(level)]);
              error(' retrieveR - heap not that large, dimensions?')
            else
              F = reshape(H(beginptr:(heaptr-1)), nF, mF);
            end
          end
        end
      end
    end
  end
end
%------------------------------------------------------------------------------


function [ opts ] = PrepareData_Char_RNN( opts )

max_char=67;

x = textread('train_x.txt');
x(:,end)=[];
opts.train = zeros(max_char, size(x,1),size(x,2));
Index=x(:)'+1+max_char*[(0:numel(x)-1)];
opts.train(Index)=1;

x = textread('train_y.txt');
opts.train_labels=x+1;

opts.n_train=size(opts.train_labels,1);

x = textread('test_x.txt');
x(:,end)=[];
opts.test = zeros(max_char, size(x ,1),size(x ,2));
Index=x (:)'+1+max_char*[(0:numel(x )-1)];
opts.test(Index)=1;

x = textread('test_y.txt');
opts.test_labels=x+1;

opts.n_test=size(opts.test_labels,1);


function magcumu() % autogenerated function wrapper
    %   This subroutine selects earthquakes in a magnitude, time
    %   and depth range for plotting cumulative curves
    %   Operates on the last subset of the catalogue (ZG.newcat).
    %   Changes ZG.newt2
    %
    %minma2 =  input('Please input Minimum Magnitude (inclusive):')
    %maxma2 =  input('Please input Maximum Magnitude:')
    %minde =  input('Please input minimum depth (inclusive):')
    %maxde =  input('Please input Maximum depth:')
    % turned into function by Celso G Reyes 2017

    ZG=ZmapGlobal.Data; % used by get_zmap_globals

    report_this_filefun(mfilename('fullpath'));
    ZG=ZmapGlobal.Data;
    % make selection from  catalogue ZG.newcat
    % ZG.newt2 is changed

    ZG.newt2 = ZG.newcat;

    l = ZG.newt2.Magnitude >= minma2 &...
        ZG.newt2.Magnitude <= maxma2 & ...
        ZG.newt2.Date >= mint & ...
        ZG.newt2.Date <= maxt;
    ZG.newt2 = ZG.newt2.subset(l);

    l = ZG.newt2.Depth >= minde &...
        ZG.newt2.Depth <= maxde ;
    ZG.newt2 = ZG.newt2.subset(l);

    %l = ZG.newt2.Date >= minti & ZG.newt2.Date <= maxti ;
    %ZG.newt2 = ZG.newt2.subset(l);

    stri = ['# ' stri1 '#  ' num2str(minma2) ' <= M <= ' num2str(maxma2) ...
        '#  ' num2str(minde) ' <= h(km) < ' num2str(maxde) ];

    ZG.t0b = min(ZG.newt2.Date);
    timeplot(ZG.newt2)
end


function test_ft_plot_box

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

figure
ft_plot_box([-1 1 2 3], 'facecolor', 'b');
axis([-4 4 -4 4])


classdef vfa_t1 < AbstractModel
% vfa_t1: Compute a T1 map using Variable Flip Angle
%
% Assumptions:
%
% Inputs:
%   VFAData         Spoiled Gradient echo data, 4D volume with different flip angles in time dimension
%   (B1map)         Normalized transmit excitation field map (B1+). B1+ is defined
%                   as a  normalized multiplicative factor such that:
%                   FA_actual = B1+ * FA_nominal. (OPTIONAL).
%   (Mask)          Binary mask to accelerate the fitting. (OPTIONAL)
%
% Outputs:
%   T1              Longitudinal relaxation time [s]
%   M0              Equilibrium magnetization
%
% Protocol:
%   VFAData Array [nbFA x 2]:
%       [FA1 TR1; FA2 TR2;...]      flip angle [degrees] TR [s]
%
% Options:
%   None
%
% Example of command line usage:
%   Model = vfa_t1;  % Create class from model
%   Model.Prot.VFAData.Mat=[3 0.015; 20 0.015]; %Protocol: 2 different FAs
%   data = struct;  % Create data structure
%   data.VFAData = load_nii_data('VFAData.nii.gz');
%   data.B1map = load_nii_data('B1map.nii.gz');
%   FitResults = FitData(data,Model); %fit data
%   FitResultsSave_mat(FitResults);
%
%   For more examples: qMRusage(vfa_t1)
%
%
% Author: Ian Gagnon, 2017
%
% References:
%   Please cite the following if you use this module:
%     Fram, E.K., Herfkens, R.J., Johnson, G.A., Glover, G.H., Karis, J.P.,
%     Shimakawa, A., Perkins, T.G., Pelc, N.J., 1987. Rapid calculation of
%     T1 using variable flip angle gradient refocused imaging. Magn. Reson.
%     Imaging 5, 201?208
%   In addition to citing the package:
%     Karakuzu A., Boudreau M., Duval T.,Boshkovski T., Leppert I.R., Cabana J.F.,
%     Gagnon I., Beliveau P., Pike G.B., Cohen-Adad J., Stikov N. (2020), qMRLab:
%     Quantitative MRI analysis, under one umbrella doi: 10.21105/joss.02343

properties (Hidden=true)
 onlineData_url = 'https://osf.io/7wcvh/download?version=3';
end

    properties
        MRIinputs = {'VFAData','B1map','Mask'};
        xnames = {'M0','T1'};
        voxelwise = 0;

        % Protocol
        Prot  = struct('VFAData',struct('Format',{{'FlipAngle' 'TR'}},...
                                         'Mat', [3 0.015; 20 0.015])); % You can define a default protocol here.

        % fitting options
        st           = [2000 0.7]; % starting point
        lb           = [0   0.00001]; % lower bound
        ub           = [6000   5]; % upper bound
        fx           = [0     0]; % fix parameters

        % Model options
        buttons = {};
        options= struct(); % structure filled by the buttons. Leave empty in the code

        % Simulation Options
        Sim_Single_Voxel_Curve_buttons = {'SNR',50};
        Sim_Optimize_Protocol_buttons = {'# of volumes',5,'Population size',100,'# of migrations',100};
    end

methods (Hidden=true)
% Hidden methods goes here.
end

    methods

        function obj = vfa_t1()
            obj.options = button2opts(obj.buttons);
        end

        function Smodel = equation(obj,x)
            % Generates a VFA signal based on input parameters
            x = mat2struct(x,obj.xnames); % if x is a structure, convert to vector

            % Equation: S=M0sin(a)*(1-E)/(1-E)cos(a); E=exp(-TR/T1)
            flipAngles = (obj.Prot.VFAData.Mat(:,1))';
            TR = obj.Prot.VFAData.Mat(1,2);
            E = exp(-TR/x.T1);
            Smodel = x.M0*sin(flipAngles/180*pi)*(1-E)./(1-E*cos(flipAngles/180*pi));
        end

       function FitResult = fit(obj,data)
            % T1 and M0
            flipAngles = (obj.Prot.VFAData.Mat(:,1))';
            TR = obj.Prot.VFAData.Mat(:,2);
            if obj.voxelwise == 0
                if (length(unique(TR))~=1), error('VFA data must have same TR'); end
                if ~isfield(data, 'B1map'), data.B1map = []; end
                if ~isfield(data, 'Mask'), data.Mask = []; end
                [FitResult.T1, FitResult.M0] = Compute_M0_T1_OnSPGR(double(data.VFAData), flipAngles, TR(1), data.B1map, data.Mask);
            elseif obj.voxelwise == 1
                if ~isfield(data,'B1map'), data.B1map=1; end
                [m0, t1] = mtv_compute_m0_t1(double(data.VFAData), flipAngles, TR(1), data.B1map);
                FitResult.T1 = t1;
                FitResult.M0 = m0;
            end
       end

       function plotModel(obj,x,data)
           if nargin<2 || isempty(x), x = obj.st; end
           x = mat2struct(x,obj.xnames);
           disp(x)
           flipAngles = obj.Prot.VFAData.Mat(:,1)';
           TR = obj.Prot.VFAData.Mat(1,2)';
           subplot(2,1,1)
           if exist('data','var')
               if isfield(data,'B1map')
                  if ~isempty(data.B1map)
                   B1map=data.B1map;
                  end
               else
                   B1map=1;
               end
                % Plot data and fitted signal
                plot(flipAngles,data.VFAData,'.','MarkerSize',16)
            else
                B1map=1;
            end
            E = exp(-TR/x.T1);
            Smodel = x.M0*sin(flipAngles/180*pi*B1map)*(1-E)./(1-E*cos(flipAngles/180*pi*B1map));
            hold on
            plot(flipAngles,Smodel,'x','MarkerSize',16)
            hold off
            title('Data points','FontSize',14);
            xlabel('Flip Angle [deg]','FontSize',12);
            ylabel('Signal','FontSize',12);
            legend('data', 'fitted','Location','best')
            set(gca,'FontSize',12)

            % Plot linear fit
            subplot(2,1,2)
            if exist('data','var')
                ydata = data.VFAData./sin(flipAngles/180*pi*B1map)';
                xdata = data.VFAData./tan(flipAngles/180*pi*B1map)';
                plot(xdata,ydata,'xb','MarkerSize',16)
                hold on
            end
            slope = exp(-TR/x.T1);
            intercept = x.M0*(1-slope);
            X=Smodel./tan(flipAngles/180*pi*B1map);
            mval = min(X);
            Mval = max(X);
            plot([mval Mval],intercept+slope*[mval Mval],'-r');
            hold off
            title(sprintf('Linear Fit: T1=%0.4f s; M0=%0.0f;',x.T1,x.M0),'FontSize',14);
            xlabel('[au]','FontSize',12);
            ylabel('[au]','FontSize',12);
            legend('linearized data', 'linear fit','Location','best')
            %txt=strcat('T1=',num2str(x.T1),'s M0=',num2str(x.M0));
            set(gca,'FontSize',12)

%             h = plot( fitresult, xData, yData,'+');
%             set(h,'MarkerSize',30)
%             legend( h, 'y vs. x', 'untitled fit 1', 'Location', 'NorthEast' );
%             p11 = predint(fitresult,x,0.95,'observation','off');
%             hold on
%             plot(x,p11,'m--'); drawnow;
%             hold off
%             % Label axes
%             xlabel( 'x' );
%             ylabel( 'y' );
%             grid on
%             saveas(gcf,['temp.jpg']);
       end
       function [FitResults, data] = Sim_Single_Voxel_Curve(obj, x, Opt,display)
           % Simulates Single Voxel
           %
           % :param x: [struct] fit parameters
           % :param Opt.SNR: [struct] signal to noise ratio to use
           % :param display: 1=display, 0=nodisplay
           % :returns: [struct] FitResults, data (noisy dataset)

           if ~exist('display','var'), display = 1; end
           Smodel = equation(obj, x);
           sigma = max(abs(Smodel))/Opt.SNR;
           data.VFAData = ricernd(Smodel,sigma)';
           data.B1map = 1;

           FitResults = fit(obj,data);
           if display
               plotModel(obj, FitResults, data);
           end
       end

       function SimVaryResults = Sim_Sensitivity_Analysis(obj, OptTable, Opt)
           % SimVaryGUI
           SimVaryResults = SimVary(obj, Opt.Nofrun, OptTable, Opt);
       end

       function SimRndResults = Sim_Multi_Voxel_Distribution(obj, RndParam, Opt)
           % SimVaryGUI
           SimRndResults = SimRnd(obj, RndParam, Opt);
       end

    end

    % CLI-only implemented static methods. Can be called directly from
    % class - no object needed.
    methods(Static)
        function Mz = analytical_solution(params)
            %ANALYTICAL_SOLUTION  Analytical equations for the longitudinal magnetization of
            %steady-state gradient echo experiments.
            %
            %   Reference: Stikov, N. , Boudreau, M. , Levesque, I. R.,
            %   Tardif, C. L., Barral, J. K. and Pike, G. B. (2015), On the
            %   accuracy of T1 mapping: Searching for common ground. Magn.
            %   Reson. Med., 73: 514-522. doi:10.1002/mrm.25135
            %
            %   params: Struct.
            %           Properties: T1, TR, EXC_FA, constant (optional)
            %

            Mz = vfa_equation(params);

        end

        function signMaxAngle = ernst_angle(params)
            %ERNST_ANGLE  Analytical equations for the longitudinal magnetization of
            %steady-state gradient echo experiments.
            %
            %   Reference: Ernst, R. R. (1966). "Application of Fourier
            %   transform spectroscopy to magnetic resonance". Review of
            %   Scientific Instruments. 37: 93. doi:10.1063/1.171996
            %
            %   params: Struct.
            %           Properties: T1, TR
            %

            try
                T1 = params.T1;
                TR = params.TR;
            catch
                error('vfa_t1.ernst_equation: Incorrect parameters.  Run `help vfa_t1.ernst_angle` for more info.')
            end

            signMaxAngle = acosd(exp(-TR./T1));

        end

        function [Mz, Msig] = bloch_sim(params)
            %BLOCH_SIM Bloch simulations of the GRE-IR pulse sequence.
            % Simulates 100 spins params. Nex repetitions of the IR pulse
            % sequences.
            %
            % params: Struct with the following fields:
            %   EXC_FA: Excitation pulse flip angle in degrees.
            %   TI: Inversion time (ms).
            %   TR: Repetition time (ms).
            %   TE: Echo time (ms).
            %   T1: Longitudinal relaxation time (ms).
            %   T2: Transverse relaxation time (ms).
            %   Nex: Number of excitations
            %
            %   (optional)
            %       df: Off-resonance frequency of spins relative to excitation pulse (in Hz)
            %       crushFlag: Numeric flag for perfect spoiling (1) or partial spoiling (2).
            %       partialDephasingFlag: do partialDephasing (see below)
            %       partialDephasing: Partial dephasing fraction (between [0, 1]). 1 = no dephasing, 0 = complete dephasing (sele
            %       inc: Phase spoiling increment in degrees.
            %
            % Outputs:
            %   Mz: Longitudinal magnetization at just prior to excitation pulse.
            %   Msig: Complex signal produced by the transverse magnetization at time TE after excitation.
            %

            %% Setup parameters
            %

            alpha = deg2rad(params.EXC_FA);
            TR = params.TR;
            T1 = params.T1;

            TE = params.TE;
            T2 = params.T2;

            Nex = params.Nex;

            %% Optional parameers

            if isfield(params, 'df')
                df = params.df;
            else
                df = 0;
            end

            if isfield(params, 'crushFlag')
                crushFlag = params.crushFlag;
            else
                crushFlag = 1;
            end

            if isfield(params, 'partialDephasingFlag')
                partialDephasingFlag = params.partialDephasingFlag;
            else
                partialDephasingFlag = 0;
            end

            if isfield(params, 'partialDephasing')
                partialDephasing = params.partialDephasing;
            else
                partialDephasing = 1;
            end

            if isfield(params, 'inc')
                inc = deg2rad(params.inc);
            else
                inc = 0;
            end

            %% Simulate for every flip angless
            %

            for ii = 1:length(alpha)

                [Msig(ii), Mz(ii)] = vfa_blochsim(                  ...
                    alpha(ii),            ...
                    T1,                   ...
                    T2,                   ...
                    TE,                   ...
                    TR,                   ...
                    crushFlag,            ...
                    partialDephasingFlag, ...
                    partialDephasing,     ...
                    df,                   ...
                    Nex,                  ...
                    inc                   ...
                    );

            end
        end

        function EXC_FA = find_two_optimal_flip_angles(params, sigFigs)
            %FIND_TWO_OPTIMAL_FLIP_ANGLES Calculate the two optimal flip
            %angles (having signals 71% of the signal at the Ernst angle).
            %
            % Simulates 100 spins params. Nex repetitions of the IR pulse
            % sequences.
            %
            % References:
            %
            % Deoni, S. C., Rutt, B. K. and Peters, T. M. (2003), Rapid
            % combined T1 and T2 mapping using gradient recalled
            % acquisition in the steady state. Magn. Reson. Med., 49:
            % 515-526.
            %
            % Schabel, M.C. & Morrell, G.R., 2009. Uncertainty in T(1)
            % mapping using the variable flip angle method with two flip
            % angles. Physics in medicine and biology, 54(1), pp.N1?8.

            if ~exist('sigFigs','var')
                sigFigs = 0;
            end

            % Set up search space of flip angle and signal values
            flipAngleSearchSpace = 0:1*10^(-sigFigs):90;

            params.EXC_FA = flipAngleSearchSpace;
            signals = vfa_t1.analytical_solution(params);

            % Get the Angle, find index in search space
            maxAngle = vfa_t1.ernst_angle(params);
            [~,maxRangeIndex] = min(abs(flipAngleSearchSpace-maxAngle));

            % Calculate signal at optimal flip angle values
            optAngleSignal= 0.71*signals(maxRangeIndex);

            % Calculate indices of optimal flip angles (smaller and larger
            % than the Ernst angle)
            [~,optRangeIndex_small] = min(abs(signals(1:maxRangeIndex)-optAngleSignal));
            [~,optRangeIndex_large_rel] = min(abs(signals(maxRangeIndex+1:end)-optAngleSignal));
            optRangeIndex_large = maxRangeIndex+optRangeIndex_large_rel;

            % Output optimal flip angle values
            EXC_FA = [flipAngleSearchSpace(optRangeIndex_small), flipAngleSearchSpace(optRangeIndex_large)];

        end
    end
end


function [SRCC,PLCC] = test_TID2013(net,testDatabase, testMethod, Trainingproportion,seed)

    if ~exist(fullfile('result', testDatabase, testMethod), 'dir')
        mkdir(fullfile('result', testDatabase, testMethod));
    end

    fprintf('Results on TID2013...');
    Path = fullfile('databases', testDatabase);
    type_scores = cell(24,1);
    type_mos = cell(24,1);

    fp = fopen(fullfile(Path,'mos_with_names.txt'),'r');
    for i = 1:3000
        temp = fgetl(fp);
        mos(i) = str2num(temp(1:7));
        classes(i) = str2num(temp(10:11));
        disImagePath{i} = fullfile(Path,'distorted_images',temp(9:end));
        type(i) = str2num(temp(13:14));
        if i <= 2880
            refImagePath{i} = fullfile(Path,'reference_images',['I' temp(10:11) '.BMP']);
        else
            refImagePath{i} = fullfile(Path,'reference_images',['i' temp(10:11) '.bmp']);
        end
    end
    fclose(fp);

    if strcmp(testMethod,'CNN')
        [~, testingSet] = generateTrainingSet(classes,seed,Trainingproportion);
        for i = 1:numel(testingSet)
            scores(i) = processOneImage_CNN(net,disImagePath{testingSet(i)});
        end
        SRCC = corr(scores', mos(testingSet)', 'type', 'Spearman');
        PLCC = RegressionTID2013(scores', mos(testingSet)');
        fprintf('SRCC = %.4f;  PLCC = %.4f  \n', abs(SRCC), abs(PLCC))
    else
        if ~exist(fullfile('result', testDatabase, testMethod, 'scores.mat'), 'file')
            for i = 1:numel(disImagePath)
                fprintf('processing image %d / %d \n',i,numel(disImagePath));
                scores(i) = CallingTheSourceCodeForScorePrediction(disImagePath{i},testMethod);
            end
            save(fullfile('result', testDatabase, testMethod, 'scores.mat'),'scores');
        else
            load(fullfile('result', testDatabase, testMethod, 'scores.mat'),'scores');
        end
        for seed = 1:10
            [~, testingSet] = generateTrainingSet(classes,seed,Trainingproportion);
            SRCC(seed) = corr(scores(testingSet)', mos(testingSet)', 'type', 'Spearman');
            PLCC(seed) = RegressionTID2013(scores(testingSet)', mos(testingSet)');
        end
        fprintf('SRCC = %.4f, std = %.4f;  PLCC = %.4f, std = %.4f \n', abs(median(SRCC)), std(SRCC), abs(median((PLCC))), std((PLCC)));
    end

end


function p = addFromProbedBinary(p)

% For a nonlinear operator, the envelope model is tighter on a smaller
% domain. Search for (binary d implies x >= L) and then compute bounds on
% f(x) on that domain, and add implies(d, L <= f(x) <= U)
if ~isempty(p.binary_variables) && ~isempty(p.F_struc)
    newCuts = zeros(0,size(p.F_struc,2));
    b = p.F_struc(:,1);
    A = p.F_struc(:,2:end);
    D = A(:,p.binary_variables);
    A(:,p.binary_variables) = 0;
    probedBoundsL = [];
    probedBoundsU = [];
    for i = p.K.f + 1:p.K.f + p.K.l
        [~,ix,valx] = find(A(i,:));
        if length(ix) == 1
            [~,id,vald] = find(D(i,:));
            if length(id) == 1
                if valx > 0
                    % b + valx*x + vald*d >= 0
                    % valx*x >= (-b-vald*x)
                    % d implies x >= (-b-vald)/valx
                    activated_lower = (-b(i)-vald)/valx;
                    probedBoundsL(end+1,1) = id;
                    probedBoundsL(end,2) = ix;
                    probedBoundsL(end,3) = activated_lower;
                elseif valx < 0
                    % b - (-valx)*x + vald*d >= 0
                    % b + vald*d >= -valx*x
                    % d implies (b+vald/(-valx)
                    activated_upper = (b(i)+vald)/(-valx);
                    probedBoundsU(end+1,1) = id;
                    probedBoundsU(end,2) = ix;
                    probedBoundsU(end,3) = activated_upper;
                end
            end
        end
    end
    if ~isempty(probedBoundsL) || ~isempty(probedBoundsU)
        for i = 1:length(p.binary_variables)
            iL = find(probedBoundsL(:,1) == i);
            iU = find(probedBoundsU(:,1) == i);
            if ~isempty(iL) || ~isempty(iU)
                ptemp = p;
                if ~isempty(iL)
                    for k = iL
                        ptemp.lb(probedBoundsL(k,2)) = probedBoundsL(k,3);
                    end
                end
                if ~isempty(iU)
                    for k = iU
                        ptemp.ub(probedBoundsU(k,2)) = probedBoundsU(k,3);
                    end
                end
                ptemp.lb(p.binary_variables(i)) = 1;
                pprobed = update_monomial_bounds(ptemp);
                pprobed = propagate_bounds_from_evaluations(pprobed);
                sL = find(pprobed.lb > ptemp.lb+1e-6);
                sU = find(pprobed.ub < ptemp.ub-1e-6);
                for j = 1:length(sL)
                    % {d implies x(s) >= q}
                    % x(s) >= q*d + L*(1-d)
                    % -L + x(s) + d*(L-q) >= 0
                    if ~ismember( sL(j),p.binary_variables)   % {d implies x(s) <= q}
                        q = pprobed.lb(sL(j));
                        L = p.lb(sL(j));
                        if ~isinf(q) && ~isinf(L)
                            newCuts(end+1,1) = -L;
                            newCuts(end,1+sL(j)) = 1;
                            newCuts(end,1+p.binary_variables(i)) = L-q;
                        end
                    end
                end
                for j = 1:length(sU)
                    if ~ismember( sU(j),p.binary_variables)   % {d implies x(s) <= q}
                        % x(s) <= q*d + U*(1-d)
                        % U - x(s) + d*(q-U) >= 0
                        q = pprobed.ub(sU(j));
                        U = p.ub(sU(j));
                        if ~isinf(q) && ~isinf(U)
                            newCuts(end+1,1) = U;
                            newCuts(end,1+sU(j)) = -1;
                            newCuts(end,1+p.binary_variables(i)) = q-U;
                        end
                    end
                end
            end
        end
    end
    if nnz(newCuts)>0
        p.F_struc = [p.F_struc(1:p.K.f+p.K.l,:);
            newCuts;
            p.F_struc(p.K.f+p.K.l+1:end,:)];
        p.K.l = p.K.l + size(newCuts,1);
    end
end


function [output, firstIndexPoint] = read_bti_hs( filename, outfile)

%read_hs_file Reads in BTI-Headshape files
%   filename: file with the headshape informations
%   outfile: if present, a ctf ".shape" file is written
%   output: if present, a 3xN matrix containing the headshape-points
%
%   (C) 2007 by Thomas Hartmann

% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

if nargin == 1
    outfile = [];
end %if

fid       = fopen_or_error(filename, 'r', 'b');
version   = fread(fid, 1, '*uint32');
timestamp = fread(fid, 1, '*int32');
checksum  = fread(fid, 1, '*int32');
nPoints   = fread(fid, 1, '*int32');

firstIndexPoint = fread(fid, [3, 5], 'double')';

points = fread(fid, [3, double(nPoints)], 'double');

fclose(fid);

if(nargout > 0)
    output = points';
end %if

if(nargin == 2)
    fid = fopen_or_error(outfile, 'wt');
    fprintf(fid, '%d\n', nPoints);
    for i = 1:size(points, 2)
        fprintf(fid, '%.3f\t%.3f\t%.3f\n', points(1, i), points(2, i), points(3, i));
    end %for
    fclose(fid);

end %if


function Y = clfKnnFwd( clf, X )
% Apply a k-nearest neighbor classifier to X.
%
% USAGE
%  Y = clfKnnFwd( clf, X )
%
% INPUTS
%  clf     - trained model
%  X       - nxp data array
%
% OUTPUTS
%  Y       - nx1 vector of labels predicted according to the clf
%
% EXAMPLE
%
% See also CLFKNN, CLFKNNTRAIN
%
% Piotr's Image&Video Toolbox      Version 2.0
% Copyright 2008 Piotr Dollar.  [pdollar-at-caltech.edu]
% Please email me if you find bugs, or have suggestions or questions!
% Licensed under the Lesser GPL [see external/lgpl.txt]

if( ~strcmp(clf.type,'knn')); error( ['incorrect type: ' clf.type] ); end
if( size(X,2)~= clf.p ); error( 'Incorrect data dimension' ); end

metric = clf.metric;
Xtrain = clf.Xtrain;
Ytrain = clf.Ytrain;
k = clf.k;

% get nearest neighbors for each X point
D = pdist2( X, Xtrain, metric );
Y = clfKnnDist( D, Ytrain, k );