%% snapSubInd
% Below is a demonstration of the features of the |snapSubInd| function

%%
clear; close all; clc;

%% Syntax
% |[subIndOut,L_valid]=snapSubInd(subIndIn,siz);|

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


classdef AoATermConditionOptimizationVariable < AbstractOptimizationVariable
    %AoATermConditionOptimizationVariable Summary of this class goes here
    %   Detailed explanation goes here

    properties
        varObj(1,1) AngleOfAttackTermCondition = AngleOfAttackTermCondition(0);

        lb(1,1) double = 0;
        ub(1,1) double = 0;

        useTf(1,1) = false;
    end

    methods
        function obj = AoATermConditionOptimizationVariable(varObj)
            obj.varObj = varObj;
            obj.varObj.optVar = obj;

            obj.id = rand();
        end

        function x = getXsForVariable(obj)
            x = [];

            if(obj.useTf)
                x = obj.varObj.targetAoA;
            end
        end

        function [lb, ub] = getBndsForVariable(obj)
            lb = obj.lb(obj.useTf);
            ub = obj.ub(obj.useTf);
        end

        function [lb, ub] = getAllBndsForVariable(obj)
            lb = obj.lb;
            ub = obj.ub;
        end

        function setBndsForVariable(obj, lb, ub)
            obj.lb = lb;
            obj.ub = ub;
        end

        function useTf = getUseTfForVariable(obj)
            useTf = obj.useTf;
        end

        function setUseTfForVariable(obj, useTf)
            obj.useTf = useTf;
        end

        function updateObjWithVarValue(obj, x)
            obj.varObj.targetAoA = x;
        end

        function nameStrs = getStrNamesOfVars(obj, evtNum, varLocType)
            nameStrs = {sprintf('Event %i Angle Of Attack Termination Condition', evtNum)};
        end

        function varsStoredInRad = getVarsStoredInRad(obj)
            varsStoredInRad = true;
        end
    end
end


function matlabVal = java2matlab(javaVal)

  if (strcmp(class(javaVal), 'java.util.Vector') == 1)
    if (javaVal.size() ~= 2)
      error('Incorrect Vector size (~= 2)');
    end
    fieldType=javaVal.get(0);
    value=javaVal.get(1);

    switch fieldType
     case 1 % function_handle
      matlabVal=str2func(value(2:end));

     case 2 % struct
      if (strcmp(class(value), 'java.util.Vector') == 0)
        error('Struct array value is not a java.util.Vector');
      end
      if (value.size() < 1)
        error('Struct array is empty.');
      end
      dim = value.get(0);
      matlabVal=struct('x', cell(dim'));
      matlabVal=rmfield(matlabVal, 'x');

      for k=1:numel(matlabVal)
        struc = value.get(k);
        keys= struc.keySet();
        iterator=keys.iterator();

        while iterator.hasNext()
          key=iterator.next();
          val=struc.get(key);
          matlabVal(k).(key)=java2matlab(val);
        end
      end

     case 3 % cell
      if (strcmp(class(value), 'java.util.Vector') == 0)
        error('Cell array value is not a java.util.Vector');
      end
      if (value.size() < 1)
        error('Java cell-array vector is empty.');
      end
      dim=value.get(0);
      matlabVal=cell(dim');
      if (value.size() < numel(matlabVal)+1)
        error('Java cell-array vector is too small.');
      end
      for i=1:numel(matlabVal)
        matlabVal{i} = java2matlab(value.get(i));
      end

     case 4 % matlab matrix
      if (strcmp(class(value), 'java.util.Vector') == 0)
        error('Matrix value is not a java.util.Vector');
      end
      if (value.size() < 1)
        error('Java matrix vector is empty.');
      end
      dim=value.get(0);
      matlabVal=reshape(value.get(1), reshape(dim,1,length(dim)));

     case 5 % java
      matlabVal = value;

     case 6 % slaveRef
      matlabVal = slaveRef([], value);

    end
  else
    matlabVal = javaVal;
  end


classdef AddMassToTankActionOptimVar < AbstractOptimizationVariable
    %AddMassToTankActionOptimVar Summary of this class goes here
    %   Detailed explanation goes here

    properties
        varObj(1,1) AddMassToTankAction = AddMassToTankAction();

        lb(1,1) double = 0;
        ub(1,1) double = 0;

        useTf(1,1) = false;
    end

    methods
        function obj = AddMassToTankActionOptimVar(varObj)
            obj.varObj = varObj;
            obj.varObj.optVar = obj;

            obj.id = rand();
        end

        function x = getXsForVariable(obj)
            x = [];

            if(obj.useTf)
                x = obj.varObj.massToAdd;
            end
        end

        function [lb, ub] = getBndsForVariable(obj)
            lb = obj.lb(logical(obj.useTf));
            ub = obj.ub(logical(obj.useTf));
        end

        function [lb, ub] = getAllBndsForVariable(obj)
            lb = obj.lb;
            ub = obj.ub;
        end

        function setBndsForVariable(obj, lb, ub)
            obj.lb = lb;
            obj.ub = ub;
        end

        function useTf = getUseTfForVariable(obj)
            useTf = logical(obj.useTf);
        end

        function setUseTfForVariable(obj, useTf)
            obj.useTf = logical(useTf);
        end

        function updateObjWithVarValue(obj, x)
            obj.varObj.massToAdd = x;
        end

        function nameStrs = getStrNamesOfVars(obj, evtNum, varLocType)
            nameStrs = {sprintf('Event %i Add Mass To Tank Action', evtNum)};
        end
    end
end


function compDDSampling( aid )
%COMPDDSAMPLING Summary of this function goes here
%   Detailed explanation goes here
global config;
bufname = config.bufname;

% load([bufname config.objHypoFile3D],'CAN_POOL');
A = load([bufname config.roomModelFile],'orientation','surfacelabel_ori','colorName_rot');
omap = A.orientation;
gc = A.surfacelabel_ori;
cn = A.colorName_rot;
clear A;

% bufName = sprintf([bufname '/' config.objTestImgFeature], aid);
bufName = [bufname '/' config.objTestImgFeature];
% gndBufName = sprintf([bufname '/' config.objGndImgFeature], aid);
gndBufName = [bufname '/' config.objGndImgFeature];
load([bufname config.vpEstimationFile]);
load(config.annotationfile);
H2G_R = ANNO_ALL(aid).ANNO3D.R * ANNO_ALL(aid).ANNO3D.Rc / R;
clear ANNO_ALL;

load(config.groundtruthfile);
gnd = ALL_GNDS_ROT{aid,1};
GND_COMP = getHypInfo({gnd});
clear ALL_GNDS_ROT
load(config.roomtransformfile);
VALID_TRANS_GNDS = TRANS_GNDS(config.anno_valid);
clear TRANS_GNDS

load([bufname config.objHypoFile3D]);
ALL_POOL = CAN_POOL;
clear CAN_POOL

rot_file_name = [bufname config.IMGLIST(aid).name config.rotateImageSuffix];
rotImg = imread(rot_file_name);

%% sample
for rid = 1:length(ALL_POOL)
    fprintf('Room ID: %d\n', rid);
    if config.USEPREVIOUS
        if exist(sprintf([bufname '/' config.sceneFeatFile], rid), 'file') ...
                && exist(sprintf([bufname '/' config.objFeatFile], rid), 'file')
            continue;
        end
    end

    CAN_POOL = ALL_POOL(rid);
    if isempty(CAN_POOL{1})
        objfea = [];
        roomfea = [];
        anglesid = [];
        ALL_HYPS = [];
        if config.UPDATE_TO_DISK
            save(sprintf([bufname '/' config.objFeatFile], rid),'objfea','roomfea','anglesid');
            save(sprintf([bufname '/' config.sceneFeatFile], rid), 'ALL_HYPS');
        end
        fprintf('Empty pool, done.\n');
        continue;
    end

    [objfea, roomfea, anglesid] = compObjHypsFeature( rotImg, omap, gc, cn, CAN_POOL{1}, config.typenum, bufName );
    if config.UPDATE_TO_DISK
        save(sprintf([bufname '/' config.objFeatFile], rid),'objfea','roomfea','anglesid');
    end

    CAN_POOL{1}.sel_hyps.objfea = objfea;
    CAN_POOL{1}.room.roomfea = roomfea;
    CAN_POOL{1}.sel_hyps.anglesid = anglesid;
    ALL_HYPS = globalSampling( CAN_POOL{1}.sel_hyps, config.samplenum, config.typenum );
    [ sceneImgFea ] = compSceneHypsFeatureA( ALL_HYPS, CAN_POOL{1}.sel_hyps, CAN_POOL{1}.room, omap, gc, cn );
    for i = 1:config.samplenum
        ALL_HYPS(i).sceneImgFea = sceneImgFea(i,:);
    end

    ALL_SCENE = packupScene(ALL_HYPS, CAN_POOL{1});
    [ MINSCORE, MINTRANS] = compRoomMatchScore( ALL_SCENE, VALID_TRANS_GNDS );
    for i = 1:length(ALL_SCENE)
        ALL_HYPS(i).MINSCORE = MINSCORE(i,:);
        ALL_HYPS(i).MINTRANS = MINTRANS(i,:);
    end

    if strcmp(config.lossfunc, 'cost')
        for i = 1:length(ALL_SCENE)
            if isempty(ALL_SCENE{i})
                ALL_HYPS(i).COST = 4.5;
                continue;
            end
            ALL_HYPS(i).COST = roomLossFunction3D( gnd, ALL_SCENE{i}, H2G_R );
        end
    elseif strcmp(config.lossfunc, 'align')
        for i = 1:length(ALL_SCENE)
            if isempty(ALL_SCENE{i})
                ALL_HYPS(i).COST = 4.5;
                continue;
            end
            HYP_COMP = getHypInfo(ALL_SCENE{i});
            ALL_HYPS(i).COST = roomAlignmentMex(HYP_COMP, GND_COMP, 3, 0);
        end
    end
    if config.UPDATE_TO_DISK
        save(sprintf([bufname '/' config.sceneFeatFile], rid), 'ALL_HYPS');
    end
end

% sample near ground truth, only for training
if config.SAMPLE_CLOSE_GND
    [GOOD_HYPS, BESTRHID] = gndtruthSampling( ALL_POOL, gnd, config.samplenum );
    load(sprintf([bufname '/' config.objFeatFile], BESTRHID));
    ALL_POOL{BESTRHID}.sel_hyps.objfea = objfea;
    ALL_POOL{BESTRHID}.room.roomfea = roomfea;
    ALL_POOL{BESTRHID}.sel_hyps.anglesid = anglesid;
    [ sceneImgFea ] = compSceneHypsFeatureA( GOOD_HYPS, ALL_POOL{BESTRHID}.sel_hyps, ALL_POOL{BESTRHID}.room, omap, gc, cn );
    for i = 1:config.samplenum
        GOOD_HYPS(i).sceneImgFea = sceneImgFea(i,:);
    end

    ALL_SCENE = packupScene( GOOD_HYPS, ALL_POOL{BESTRHID});
    [ MINSCORE, MINTRANS] = compRoomMatchScore( ALL_SCENE, VALID_TRANS_GNDS );
    for j = 1:length(ALL_SCENE)
        GOOD_HYPS(j).MINSCORE = MINSCORE(j,:);
        GOOD_HYPS(j).MINTRANS = MINTRANS(j,:);
    end

    if strcmp(config.lossfunc, 'cost')
        for i = 1:length(ALL_SCENE)
            if isempty(ALL_SCENE{i})
                GOOD_HYPS(i).COST = 4.5;
                continue;
            end
            GOOD_HYPS(i).COST = roomLossFunction3D( gnd, ALL_SCENE{i}, H2G_R );
        end
    elseif strcmp(config.lossfunc, 'align')
        for i = 1:length(ALL_SCENE)
            if isempty(ALL_SCENE{i})
                GOOD_HYPS(i).COST = 4.5;
                continue;
            end
            HYP_COMP = getHypInfo(ALL_SCENE{i});
            GOOD_HYPS(i).COST = roomAlignmentMex(HYP_COMP, GND_COMP, 3, 0);
        end
    end

    % compute ground truth
    gnd_rotImg = imread([bufname '/' config.IMGLIST(aid).name config.gndImageSuffix]);
    gnd_omap = rotatePanorama( omap, H2G_R);
    gnd_gc = rotatePanorama( gc, H2G_R);
    gnd_cn = rotatePanorama( cn, H2G_R);

    [ GND_HYPS, ~ ] = groundTruthHypothesisNewFunc( gnd, gnd_omap, gnd_gc, gnd_cn, gnd_rotImg, config.typenum, gndBufName);
    [ MINSCORE, MINTRANS] = compRoomMatchScore( {gnd}, VALID_TRANS_GNDS );
    GND_HYPS.MINSCORE = MINSCORE;
    GND_HYPS.MINTRANS = MINTRANS;
    GND_HYPS.COST = 0;
    if config.UPDATE_TO_DISK
        save([bufname config.goodSceneFeatFile], 'GOOD_HYPS', 'GND_HYPS', 'BESTRHID');
    end
end

end


function obj = updateSurfl(obj, surfaceIndex)

%-AXIS INDEX-%
axIndex = obj.getAxisIndex(obj.State.Plot(surfaceIndex).AssociatedAxis);

%-CHECK FOR MULTIPLE AXES-%
[xsource, ysource] = findSourceAxis(obj,axIndex);

%-SURFACE DATA STRUCTURE- %
meshData = get(obj.State.Plot(surfaceIndex).Handle);
figureData = get(obj.State.Figure.Handle);

%-AXIS STRUCTURE-%
axisData = get(ancestor(meshData.Parent,'axes'));

%-SCENE DATA-%
eval( sprintf('scene = obj.layout.scene%d;', xsource) );

%-GET CONTOUR INDEX-%
obj.PlotOptions.nPlots = obj.PlotOptions.nPlots + 1;
contourIndex = obj.PlotOptions.nPlots;
obj.PlotOptions.contourIndex(surfaceIndex) = contourIndex;

%-------------------------------------------------------------------------%

%-associate scene-%
obj.data{surfaceIndex}.scene = sprintf('scene%d', xsource);
obj.data{contourIndex}.scene = sprintf('scene%d', xsource);

%-------------------------------------------------------------------------%

%-surface type for face color-%
obj.data{surfaceIndex}.type = 'surface';

%-scatter3d type for contour mesh lines-%
obj.data{contourIndex}.type = 'scatter3d';
obj.data{contourIndex}.mode = 'lines';

%-------------------------------------------------------------------------%

%-get plot data-%
xData = meshData.XData;
yData = meshData.YData;
zData = meshData.ZData;

%-reformat data to mesh-%
xDataSurface = xData;
yDataSurface = yData;
zDataSurface = zData;

xDataContourDir1 = [xDataSurface; NaN(1, size(xDataSurface, 2))];
yDataContourDir1 = [yDataSurface; NaN(1, size(yDataSurface, 2))];
zDataContourDir1 = [zDataSurface; NaN(1, size(zDataSurface, 2))];

xDataContourDir2 = xDataContourDir1(1:end-1,:)';
yDataContourDir2 = yDataContourDir1(1:end-1,:)';
zDataContourDir2 = zDataContourDir1(1:end-1,:)';

xDataContourDir2 = [xDataContourDir2; NaN(1, size(xDataContourDir2, 2))];
yDataContourDir2 = [yDataContourDir2; NaN(1, size(yDataContourDir2, 2))];
zDataContourDir2 = [zDataContourDir2; NaN(1, size(zDataContourDir2, 2))];

xDataContour = [xDataContourDir1(:); xDataContourDir2(:)];
yDataContour = [yDataContourDir1(:); yDataContourDir2(:)];
zDataContour = [zDataContourDir1(:); zDataContourDir2(:)];

%-------------------------------------------------------------------------%

%-set data on surface-%
obj.data{surfaceIndex}.x = xDataSurface;
obj.data{surfaceIndex}.y = yDataSurface;
obj.data{surfaceIndex}.z = zDataSurface;

%- setting grid mesh by default -%
% x-direction
xData = xData(1, :);
obj.data{surfaceIndex}.contours.x.start = xData(1);
obj.data{surfaceIndex}.contours.x.end = xData(end);
obj.data{surfaceIndex}.contours.x.size = mean(diff(xData));
obj.data{surfaceIndex}.contours.x.show = true;

% y-direction
yData = yData(:, 1);
obj.data{surfaceIndex}.contours.y.start = yData(1);
obj.data{surfaceIndex}.contours.y.end = yData(end);
obj.data{surfaceIndex}.contours.y.size = mean(diff(yData));;
obj.data{surfaceIndex}.contours.y.show = true;

%-------------------------------------------------------------------------%

%-set data on scatter3d-%
obj.data{contourIndex}.x = xDataContour(:);
obj.data{contourIndex}.y = yDataContour(:);
obj.data{contourIndex}.z = zDataContour(:);

%-------------------------------------------------------------------------%

%-COLORING-%

%-------------------------------------------------------------------------%

%-get colormap-%
cMap = figureData.Colormap;
fac = 1/(length(cMap)-1);
colorScale = {};

for c = 1: length(cMap)
    colorScale{c} = { (c-1)*fac , sprintf('rgb(%f,%f,%f)', 255*cMap(c, :))};
end

%-------------------------------------------------------------------------%

%-get edge color-%
if isnumeric(meshData.EdgeColor)
    cDataContour = sprintf('rgb(%f,%f,%f)', 255*meshData.EdgeColor);

elseif strcmpi(meshData.EdgeColor, 'interp')
    cDataContour = zDataContour(:);
    obj.data{contourIndex}.line.colorscale = colorScale;

    obj.data{surfaceIndex}.contours.x.show = false;
    obj.data{surfaceIndex}.contours.y.show = false;

elseif strcmpi(meshData.EdgeColor, 'flat')
    cData = meshData.CData;

    if size(cData, 3) ~= 1
        cMap = unique( reshape(cData, ...
            [size(cData,1)*size(cData,2), size(cData,3)]), 'rows' );
        cData = rgb2ind(cData, cMap);

        edgeColorScale = {};
        fac = 1/(length(cMap)-1);

        for c = 1: length(cMap)
            edgeColorScale{c} = { (c-1)*fac , sprintf('rgb(%f,%f,%f)', 255*cMap(c, :))};
        end

        obj.data{surfaceIndex}.line.cmin = 0;
        obj.data{surfaceIndex}.line.cmax = 255;
        obj.data{contourIndex}.line.colorscale = edgeColorScale;
    else
        obj.data{contourIndex}.line.cmin = axisData.CLim(1);
        obj.data{contourIndex}.line.cmax = axisData.CLim(2);
        obj.data{contourIndex}.line.colorscale = colorScale;
    end

    cDataContourDir1 = [cData; NaN(1, size(cData, 2))];
    cDataContourDir2 = cDataContourDir1(1:end-1,:)';
    cDataContourDir2 = [cDataContourDir2; NaN(1, size(cDataContourDir2, 2))];
    cDataContour = [cDataContourDir1(:); cDataContourDir2(:)];

    obj.data{surfaceIndex}.contours.x.show = false;
    obj.data{surfaceIndex}.contours.y.show = false;

elseif strcmpi(meshData.EdgeColor, 'none')
    cDataContour = 'rgba(0,0,0,0)';
    obj.data{surfaceIndex}.contours.x.show = false;
    obj.data{surfaceIndex}.contours.y.show = false;

end

%-set edge color-%
obj.data{contourIndex}.line.color = cDataContour;
obj.data{surfaceIndex}.contours.x.color = cDataContour;
obj.data{surfaceIndex}.contours.y.color = cDataContour;

%-------------------------------------------------------------------------%

%-get face color-%
faceColor = meshData.FaceColor;

if isnumeric(faceColor)

    if all(faceColor == [1, 1, 1])
        faceColor = [0.96, 0.96, 0.96];
    end

    for n = 1:size(zDataSurface, 2)
        for m = 1:size(zDataSurface, 1)
            cDataSurface(m, n, :) = faceColor;
        end
    end

    [cDataSurface, cMapSurface] = rgb2ind(cDataSurface, 256);
    cDataSurface = double(cDataSurface) + axisData.CLim(1);

    for c = 1: size(cMapSurface, 1)
        colorScale{c} = { (c-1)*fac , sprintf('rgba(%f,%f,%f, 1)', cMapSurface(c, :))};
    end

    obj.data{surfaceIndex}.cmin = axisData.CLim(1);
    obj.data{surfaceIndex}.cmax = axisData.CLim(2);

elseif strcmpi(faceColor, 'interp')
    cDataSurface = zDataSurface;

    if surfaceIndex > xsource
        cData = [];

        for idx = xsource:surfaceIndex
            cData = [cData; obj.data{idx}.z];
        end

        cMin = min(cData(:));
        cMax = max(cData(:));

        for idx = xsource:surfaceIndex
            obj.data{idx}.cmin = cMin;
            obj.data{idx}.cmax = cMax;
        end
    end

elseif strcmpi(faceColor, 'flat')
    cData = meshData.CData;

    if size(cData, 3) ~= 1
        cMap = unique( reshape(cData, ...
            [size(cData,1)*size(cData,2), size(cData,3)]), 'rows' );
        cDataSurface = rgb2ind(cData, cMap);

        colorScale = {};
        fac = 1/(length(cMap)-1);

        for c = 1: length(cMap)
            colorScale{c} = { (c-1)*fac , sprintf('rgb(%f,%f,%f)', 255*cMap(c, :))};
        end
    else
        cDataSurface = cData;
        obj.data{surfaceIndex}.cmin = axisData.CLim(1);
        obj.data{surfaceIndex}.cmax = axisData.CLim(2);
    end

end

%-set face color-%
obj.data{surfaceIndex}.colorscale = colorScale;
obj.data{surfaceIndex}.surfacecolor = cDataSurface;

%-lighting settings-%

if isnumeric(meshData.FaceColor) && all(meshData.FaceColor == [1, 1, 1])
    obj.data{surfaceIndex}.lighting.diffuse = 0.5;
    obj.data{surfaceIndex}.lighting.ambient = 0.725;
else
    % obj.data{surfaceIndex}.lighting.diffuse = 1.0;
    % obj.data{surfaceIndex}.lighting.ambient = 0.9;
end

if meshData.FaceAlpha ~= 1
    obj.data{surfaceIndex}.lighting.diffuse = 0.5;
    obj.data{surfaceIndex}.lighting.ambient = 0.725 + (1-meshData.FaceAlpha);
end

if obj.PlotlyDefaults.IsLight
    obj.data{surfaceIndex}.lighting.diffuse = 1.0;
    obj.data{surfaceIndex}.lighting.ambient = 0.3;
end

%-opacity-%
obj.data{surfaceIndex}.opacity = meshData.FaceAlpha;

%-------------------------------------------------------------------------%

%-line style-%

obj.data{contourIndex}.line.width = 3*meshData.LineWidth;

if strcmpi(meshData.LineStyle, '-')
    obj.data{contourIndex}.line.dash = 'solid';
else
    obj.data{contourIndex}.line.dash = 'dot';
    obj.data{surfaceIndex}.contours.x.show = false;
    obj.data{surfaceIndex}.contours.y.show = false;
end

%-------------------------------------------------------------------------%

%-SCENE CONFIGUTATION-%

%-------------------------------------------------------------------------%

%-aspect ratio-%
asr = obj.PlotOptions.AspectRatio;

if ~isempty(asr)
    if ischar(asr)
        scene.aspectmode = asr;
    elseif isvector(ar) && length(asr) == 3
        xar = asr(1);
        yar = asr(2);
        zar = asr(3);
    end
else

    %-define as default-%
    xar = max(xData(:));
    yar = max(yData(:));
    xyar = max([xar, yar]);
    zar = 0.75*xyar;
end

scene.aspectratio.x = 1.1*xyar;
scene.aspectratio.y = 1.0*xyar;
scene.aspectratio.z = zar;

%---------------------------------------------------------------------%

%-camera eye-%
ey = obj.PlotOptions.CameraEye;

if ~isempty(ey)
    if isvector(ey) && length(ey) == 3
        scene.camera.eye.x = ey(1);
        scene.camera.eye.y = ey(2);
        scene.camera.eye.z = ey(3);
    end
else

    %-define as default-%
    xey = - xyar; if xey>0 xfac = -0.0; else xfac = 0.0; end
    yey = - xyar; if yey>0 yfac = -0.3; else yfac = 0.3; end
    if zar>0 zfac = -0.1; else zfac = 0.1; end

    scene.camera.eye.x = xey + xfac*xey;
    scene.camera.eye.y = yey + yfac*yey;
    scene.camera.eye.z = zar + zfac*zar;
end

%-------------------------------------------------------------------------%

%-scene axis configuration-%

scene.xaxis.range = axisData.XLim;
scene.yaxis.range = axisData.YLim;
scene.zaxis.range = axisData.ZLim;

scene.xaxis.tickvals = axisData.XTick;
scene.xaxis.ticktext = axisData.XTickLabel;

scene.yaxis.tickvals = axisData.YTick;
scene.yaxis.ticktext = axisData.YTickLabel;

scene.zaxis.tickvals = axisData.ZTick;
scene.zaxis.ticktext = axisData.ZTickLabel;

scene.xaxis.zeroline = false;
scene.yaxis.zeroline = false;
scene.zaxis.zeroline = false;

scene.xaxis.showline = true;
scene.yaxis.showline = true;
scene.zaxis.showline = true;

scene.xaxis.tickcolor = 'rgba(0,0,0,1)';
scene.yaxis.tickcolor = 'rgba(0,0,0,1)';
scene.zaxis.tickcolor = 'rgba(0,0,0,1)';

scene.xaxis.ticklabelposition = 'outside';
scene.yaxis.ticklabelposition = 'outside';
scene.zaxis.ticklabelposition = 'outside';

scene.xaxis.title = axisData.XLabel.String;
scene.yaxis.title = axisData.YLabel.String;
scene.zaxis.title = axisData.ZLabel.String;

scene.xaxis.tickfont.size = axisData.FontSize;
scene.yaxis.tickfont.size = axisData.FontSize;
scene.zaxis.tickfont.size = axisData.FontSize;

scene.xaxis.tickfont.family = matlab2plotlyfont(axisData.FontName);
scene.yaxis.tickfont.family = matlab2plotlyfont(axisData.FontName);
scene.zaxis.tickfont.family = matlab2plotlyfont(axisData.FontName);

%-------------------------------------------------------------------------%

%-SET SCENE TO LAYOUT-%
obj.layout = setfield(obj.layout, sprintf('scene%d', xsource), scene);

%-------------------------------------------------------------------------%

%-surface name-%
obj.data{surfaceIndex}.name = meshData.DisplayName;
obj.data{contourIndex}.name = meshData.DisplayName;

%-------------------------------------------------------------------------%

%-surface showscale-%
obj.data{surfaceIndex}.showscale = false;
obj.data{contourIndex}.showscale = false;

%-------------------------------------------------------------------------%

%-surface visible-%
obj.data{surfaceIndex}.visible = strcmp(meshData.Visible,'on');
obj.data{contourIndex}.visible = strcmp(meshData.Visible,'on');

%-------------------------------------------------------------------------%

leg = get(meshData.Annotation);
legInfo = get(leg.LegendInformation);

switch legInfo.IconDisplayStyle
    case 'on'
        showleg = true;
    case 'off'
        showleg = false;
end

obj.data{surfaceIndex}.showlegend = showleg;

%-------------------------------------------------------------------------%

end


function [output] = writeTiffData(imgdata,FileSaving,varargin)
	% Writes out TIF data.
	% Created by Jerome Lecoq in 2012
	% Separate function by Biafra Ahanonu
	% 2015.07.06 [19:29:37]
	% inputs
		%
	% outputs
		%

	% changelog
		% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
	% TODO
		%

	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	%========================
	% options.exampleOption = '';
	% get options
	% options = getOptions(options,varargin);
	% display(options)
	% unpack options into current workspace
	% fn=fieldnames(options);
	% for i=1:length(fn)
	% 	eval([fn{i} '=options.' fn{i} ';']);
	% end
	%========================

	try

		% FileSaving=fullfile(handles.Path,handles.File);
		% FromFrame=str2num(get(handles.FromFrame,'String'));
		% ToFrame=str2num(get(handles.ToFrame,'String'));
		NumberFrame=size(imgdata,3);
		TifFile = Tiff(FileSaving,'w');
		% We populate the tag on pixel format depending on the current data
		% type
		switch class(imgdata)
		    case 'logical'
		        tagstruct.SampleFormat=1;
		        tagstruct.BitsPerSample=1;
		    case 'uint16'
		        tagstruct.SampleFormat=1;
		        tagstruct.BitsPerSample=16;
		    case 'int16'
		        tagstruct.SampleFormat=2;
		        tagstruct.BitsPerSample=16;
		    case 'single'
		        tagstruct.SampleFormat=3;
		        tagstruct.BitsPerSample=32;
		    case 'double'
		        tagstruct.SampleFormat=3;
		        tagstruct.BitsPerSample=64;
		    case 'uint8'
		        tagstruct.SampleFormat=1;
		        tagstruct.BitsPerSample=8;
		    case 'int8'
		        tagstruct.SampleFormat=2;
		        tagstruct.BitsPerSample=8;
		    case 'uint32'
		        tagstruct.SampleFormat=1;
		        tagstruct.BitsPerSample=32;
		    case 'int32'
		        tagstruct.SampleFormat=2;
		        tagstruct.BitsPerSample=32;
		end

		CompressionScheme = 1;
		% We populate compression scheme
		switch CompressionScheme
		    case 1
		        % No compression
		        tagstruct.Compression=1;
		    case 2
		        % LZW lossless
		        tagstruct.Compression=5;
		    case 3
		        % Packbits lossless
		        tagstruct.Compression=32773;
		    case 4
		        % Deflate lossless
		        tagstruct.Compression=32946;
		end

		tagstruct.ImageLength = size(imgdata,1);
		tagstruct.ImageWidth = size(imgdata,2);
		tagstruct.Photometric = Tiff.Photometric.MinIsBlack;
		tagstruct.SamplesPerPixel = 1;
		tagstruct.PlanarConfiguration = Tiff.PlanarConfiguration.Chunky;
		tagstruct.Software = 'MATLAB';

		for i=1:NumberFrame
		    TifFile.setTag(tagstruct);
		    TifFile.write(imgdata(:,:,i));
		    TifFile.writeDirectory();
		end
		TifFile.close();

	catch err
		display(repmat('@',1,7))
		disp(getReport(err,'extended','hyperlinks','on'));
		display(repmat('@',1,7))
	end
end


function str = cfg_textfill(obj, left, right, tflag)

% function str = cfg_textfill(obj, left, right)
% Fill a text object, so that the left part is left justified and the
% right part right justified. If tflag is set, try to fit text in widget
% by truncating right until at least 5 characters are displayed.
%
% This code is part of a batch job configuration system for MATLAB. See
%      help matlabbatch
% for a general overview.
%_______________________________________________________________________
% Copyright (C) 2007 Freiburg Brain Imaging

% Volkmar Glauche
% $Id: cfg_textfill.m 7473 2018-11-06 10:26:44Z guillaume $

rev = '$Rev: 7473 $'; %#ok

if ~ishandle(obj)
    cfg_message('matlabbatch:usage',...
          'First input must be a graphics handle.');
else
    try
        get(obj,'string');
    catch
        cfg_message('matlabbatch:usage',...
              'Input object must have a ''string'' property.');
    end;
end;
if ~iscellstr(left)
    if ischar(left)
        left = cellstr(left);
    else
        cfg_message('matlabbatch:usage',...
              'Second input must be a string array or cellstr.');
    end;
    % add one space as delimiter
    left = strcat(left, {' '});
end;
if ~iscellstr(right)
    if ischar(right)
        right = cellstr(right);
    else
        cfg_message('matlabbatch:usage',...
              'Third input must be a string array or cellstr.');
    end;
end;
if numel(left) ~= numel(right)
    cfg_message('matlabbatch:usage',...
          'Second and third input must have the same number of lines.');
end;

if exist('OCTAVE_VERSION', 'builtin'), set(0,'currentfigure',ancestor(obj,'figure')); end
TempObj=copyobj(obj,get(obj,'Parent'));
set(TempObj,'Visible','off','Max',100);

% Find max extent of left string
lext = cfg_maxextent(TempObj, left);
mlext = max(lext);

% Find max extent of right string
rext = cfg_maxextent(TempObj, right);
mrext = max(rext);

% Find extent of single space
% Work around MATLAB inaccuracy by measuring 100 spaces and dividing by 100
spext = cfg_maxextent(TempObj, {repmat(' ',1,100)})/100;

% try to work out slider size
pos = get(TempObj,'Position');
oldun = get(TempObj,'units');
set(TempObj,'units','points');
ppos = get(TempObj,'Position');
set(TempObj,'units',oldun);
sc = pos(3)/ppos(3);
% assume slider width of 15 points
swidth=15*sc;

pos = get(obj,'Position');
width = max(mlext+mrext,pos(3)-swidth);
if tflag && mlext+mrext > pos(3)-swidth
    newrext = max(5*spext, pos(3)-swidth-mlext);
    trind = find(rext > newrext);
    for k = 1:numel(trind)
        tright = right{trind(k)};
        set(TempObj,'String',['...' tright]);
        ext = get(TempObj,'Extent');
        while ext(3) > newrext
            tright = tright(2:end);
            set(TempObj,'String',['...' tright]);
            ext = get(TempObj,'Extent');
        end;
        right{trind(k)} = ['...' tright];
        rext(trind(k)) = ext(3);
    end;
    % Re-estimate max extent of trimmed right string
    rext = cfg_maxextent(TempObj, right);
    mrext = max(rext);
    width = mlext+mrext;
end;

fillstr = cell(size(left));
for k = 1:numel(left)
    fillstr{k} = repmat(' ',1,floor((width-(lext(k)+rext(k)))/spext));
end;
str = strcat(left, fillstr, right);
delete(TempObj);


%                                                  y = invcholfac(u,K, perm)
% INVCHOLFAC  Computes y(perm,perm) = u' * u, with u upper triangular.
%
% ******************** INTERNAL FUNCTION OF SEDUMI ********************
%
% See also sedumi, getada3

function y = invcholfac(u,K, perm) %#ok
%
% This file is part of SeDuMi 1.1 by Imre Polik and Oleksandr Romanko
% Copyright (C) 2005 McMaster University, Hamilton, CANADA  (since 1.1)
%
% Copyright (C) 2001 Jos F. Sturm (up to 1.05R5)
%   Dept. Econometrics & O.R., Tilburg University, the Netherlands.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% Affiliation SeDuMi 1.03 and 1.04Beta (2000):
%   Dept. Quantitative Economics, Maastricht University, the Netherlands.
%
% Affiliations up to SeDuMi 1.02 (AUG1998):
%   CRL, McMaster University, Canada.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% 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 2 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, write to the Free Software
% Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
% 02110-1301, USA

disp('The SeDuMi binaries are not installed.')
disp('In Matlab, launch "install_sedumi" in the folder you put the SeDuMi files.')
disp('For more information see the file Install.txt.')
error(' ')


function [info] = decode_fif(orig)

% DECODE_FIF is a helper function for real-time processing of Neuromag data. This
% function is used to decode the content of the optional neuromag_fif chunk(s).
%
% See also DECODE_RES4, DECODE_NIFTI1, SAP2MATLAB

% Copyright (C) 2013 Arjen Stolk & Robert Oostenveld
%
% 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$

% check that the required low-level toolbox is available
ft_hastoolbox('mne', 1);

global FIFF
if isempty(FIFF)
  FIFF = fiff_define_constants();
end

if isfield(orig, 'neuromag_header')
  % The binary blob was created on the little-endian Intel Linux acquisition
  % computer, whereas the default for fiff files is that they are stored in
  % big-endian byte order. MATLAB is able to swap the bytes on the fly by specifying
  % 'le" or "be" to fopen. The normal MNE fiff_open function assumes that it is big
  % endian, hence here we have to open it as little endian.

  filename = tempname;
  % write the binary blob to disk, byte-by-byte to avoid any swapping between little and big-endian content
  F = fopen_or_error(filename, 'w');
  fwrite(F, orig.neuromag_header, 'uint8');
  fclose(F);
  % read the content of the file using the standard reading functions
  [info, meas] = read_header(filename);
  % clean up the temporary file
  delete(filename);
end

% Typically, at the end of acquisition, the isotrak and hpiresult information
% is stored in the neuromag fiff container which can then (offline) be read by
% fiff_read_meas_info. However, for the purpose of head position monitoring
% (see Stolk et al., Neuroimage 2013) during acquisition, this crucial
% information requires to be accessible online. read_isotrak and read_hpiresult
% can extract information from the additionally chunked (neuromag2ft) files.

if isfield(orig, 'neuromag_isotrak')
  filename = tempname;
  % write the binary blob to disk, byte-by-byte to avoid any swapping between little and big-endian content
  F = fopen_or_error(filename, 'w');
  fwrite(F, orig.neuromag_isotrak, 'uint8');
  fclose(F);
  % read the content of the file using the standard reading functions
  [info.dig] = read_isotrak(filename);
  % clean up the temporary file
  delete(filename);
end

if isfield(orig, 'neuromag_hpiresult')
  filename = tempname;
  % write the binary blob to disk, byte-by-byte to avoid any swapping between little and big-endian content
  F = fopen_or_error(filename, 'w');
  fwrite(F, orig.neuromag_hpiresult, 'uint8');
  fclose(F);
  % read the content of the file using the standard reading functions
  [info.dev_head_t, info.ctf_head_t] = read_hpiresult(filename);
  % clean up the temporary file
  delete(filename);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [info, meas] = read_header(filename)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% open and read the file as little endian
[fid, tree] = fiff_open_le(filename); % open as little endian
[info, meas] = fiff_read_meas_info(fid, tree);
fclose(fid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [dig] = read_isotrak(filename)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
global FIFF

% open the isotrak file (big endian)
% (typically stored in meas_info dir during acquisition, no fif extension required)
[fid, tree] = fiff_open(filename);

% locate the Polhemus data
isotrak = fiff_dir_tree_find(tree,FIFF.FIFFB_ISOTRAK);

dig=struct('kind',{},'ident',{},'r',{},'coord_frame',{});
coord_frame = FIFF.FIFFV_COORD_HEAD;
if length(isotrak) == 1
  p = 0;
  for k = 1:isotrak.nent
    kind = isotrak.dir(k).kind;
    pos  = isotrak.dir(k).pos;
    if kind == FIFF.FIFF_DIG_POINT
      p = p + 1;
      tag = fiff_read_tag(fid,pos);
      dig(p) = tag.data;
    else
      if kind == FIFF.FIFF_MNE_COORD_FRAME
        tag = fiff_read_tag(fid,pos);
        coord_frame = tag.data;
      elseif kind == FIFF.FIFF_COORD_TRANS
        tag = fiff_read_tag(fid,pos);
        dig_trans = tag.data;
      end
    end
  end
end
for k = 1:length(dig)
  dig(k).coord_frame = coord_frame;
end

if exist('dig_trans','var')
  if (dig_trans.from ~= coord_frame && dig_trans.to ~= coord_frame)
    clear('dig_trans');
  end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [dev_head_t, ctf_head_t] = read_hpiresult(filename)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
global FIFF

% open the hpiresult file (big endian)
% (typically stored in meas_info dir during acquisition, no fif extension required)
[fid, tree] = fiff_open(filename);

% locate the transformation matrix
dev_head_t=[];
ctf_head_t=[];
hpi_result = fiff_dir_tree_find(tree,FIFF.FIFFB_HPI_RESULT);
if length(hpi_result) == 1
  for k = 1:hpi_result.nent
    kind = hpi_result.dir(k).kind;
    pos  = hpi_result.dir(k).pos;
    if kind == FIFF.FIFF_COORD_TRANS
      tag = fiff_read_tag(fid,pos);
      cand = tag.data;
      if cand.from == FIFF.FIFFV_COORD_DEVICE && ...
          cand.to == FIFF.FIFFV_COORD_HEAD
        dev_head_t = cand;
      elseif cand.from == FIFF.FIFFV_MNE_COORD_CTF_HEAD && ...
          cand.to == FIFF.FIFFV_COORD_HEAD
        ctf_head_t = cand;
      end
    end
  end
end


function model = xls2model(fileName, biomassRxnEquation, defaultbound)
% Reads a model from Excel spreadsheet.
%
% USAGE:
%
%    model = xls2model(fileName, biomassRxnEquation, defaultbound)
%
% INPUT:
%    fileName:              xls spreadsheet, with one 'Reaction List' and one 'Metabolite List' tab
%
% OPTIONAL INPUTS:
%    biomassRxnEquation:    .xls may have a 255 character limit on each cell,
%                           so pass the biomass reaction separately if it hits this maximum.
%
%    defaultbound:          the deault bound for lower and upper bounds, if
%                           no bounds are specified in the Excel sheet
% OUTPUT:
%    model:                 COBRA Toolbox model
%
% EXAMPLE:
%
%                   'Reaction List' tab headers (case sensitive):
%
%                     * Required:
%
%                       * 'Abbreviation':      HEX1
%                       * 'Reaction':          `1 atp[c] + 1 glc-D[c] --> 1 adp[c] + 1 g6p[c] + 1 h[c]`
%
%                     * Optional:
%
%                       * 'GPR':               (3098.3) or (80201.1) or (2645.3) or ...
%                       * 'Description':       Hexokinase
%                       * 'Subsystem':         Glycolysis
%                       * 'Reversible':        0 (false) or 1 (true)
%                       * 'Lower bound':       0
%                       * 'Upper bound':       1000
%                       * 'Objective':         0/1
%                       * 'Confidence Score':  0,1,2,3,4
%                       * 'EC Number':         2.7.1.1,2.7.1.2
%                       * 'KEGG ID':           R000001
%                       * 'Notes':             Reaction also associated with EC 2.7.1.2
%                       * 'References':        PMID:2043117,PMID:7150652,...
%
%                   'Metabolite List' tab: Required headers (case sensitive): (needs to be complete list of metabolites,
%                   i.e., if a metabolite appears in multiple compartments it has to be represented in multiple rows.
%                   Abbreviations need to overlap with use in Reaction List
%
%                     * Required
%
%                       * 'Abbreviation':      glc-D or glc-D[c]
%                     * Optional:
%
%                       * 'Charged formula' or formula:   C6H12O6
%                       * 'Charge':                       0
%                       * 'Compartment':                  cytosol
%                       * 'Description':                  D-glucose
%                       * 'KEGG ID':                      C00031
%                       * 'PubChem ID':                   5793
%                       * 'ChEBI ID':                     4167
%                       * 'InChI string':                 InChI=1/C6H12O6/c7-1-2-3(8)4(9)5(10)6(11)12-2/h2-11H,1H2/t2-,3-,4+,5-,6?/m1/s1
%                       * 'SMILES':                       OC[C@H]1OC(O)[C@H](O)[C@@H](O)[C@@H]1O
%                       * 'HMDB ID':                      HMDB00122
%
% NOTE:
%
%    Optional inputs may be required for input on unix machines.
%
% NOTE:
%
%    Find an example Excel sheet at `docs/source/examples/ExcelExample.xlsx`
%
% .. Authors:
%    - Ines Thiele, 01/02/09
%    - Richard Que, 04/27/10, Modified reading of PubChemID and ChEBIID so that if met
%      has multiple IDs, all are passed to model. Confidence Scores
%      PubChemIDs, and ChEBIIDs, are properly passed as cell arrays.
%    - Ronan Fleming, 08/17/10, Support for unix
%    - Hulda S.H., 10/11/10, Modified reading of xls document.
%      Identifies columns by their headers. Added reading of HMDB ID.

warning off

% test if Excel is available
excelInstalled = false;
try
    excelObj = actxserver('Excel.Application');
    excelInstalled = true;
    %h.WorkBooks.Item(fileName).Close;
    fprintf(' > Excel is installed.\n\n');
catch ME
    fprintf(' > Excel is not installed; Using xlread.\n\n');
end

if exist(fileName,'file') == 2
    try
        try
            [~, sheets] = xlsfinfo(fileName);
        catch ME
            if strcmp(ME.identifier,'MATLAB:xlsread:FileDoesNotExist')
                [~, sheets] = xlsfinfo(fullfile(pwd, fileName));
            else
                error(ME);
            end
        end
        if ~all(ismember({'Reaction List','Metabolite List'},sheets))
            error(['The provided Excel Sheet (', fileName,') must contain a "Reaction List" and a "Metabolite List sheet as specified here:' sprintf('\n'),...
                   'https://opencobra.github.io/cobratoolbox/docs/ExcelModelFileDefinition.html']);
        end
    catch ME
        fprintf(strrep(sheets, '\', '\\'));
    end
else
    error('File %s not found',fileName);
end

if ~exist('defaultbound','var')
    defaultbound = 1000;
end

%assumes that one has an xls file with two tabs
if isunix || ~excelInstalled
    [~, Strings, rxnInfo] = xlread(fileName,'Reaction List');
    [~, MetStrings, metInfo] = xlread(fileName,'Metabolite List');
else
    [~, Strings, rxnInfo] = xlsread(fileName, 'Reaction List');
    [~, MetStrings, metInfo] = xlsread(fileName, 'Metabolite List');
end

%trim empty row from Numbers and MetNumbers
rxnInfo = rxnInfo(1:size(Strings,1),:);
metInfo = metInfo(1:size(MetStrings,1),:);

if isunix && isempty(MetStrings)
    error('Save .xls file as Windows 95 version using gnumeric not openoffice!');
end

requiredRxnHeaders = {'Abbreviation','Reaction'};
requiredMetHeaders = {'Abbreviation'};

if ~all(ismember(requiredRxnHeaders,Strings(1,:)))
    error(['Required Headers not present in the "Reaction List" sheet of the provided xls file.', sprintf('\n'),...
           'Note, that headers are case sesnitive!', sprintf('\n'),...
           'Another likely source for this issue is a change in the xls format specification.', sprintf('\n'),...
           'Please have a look at the specification at https://opencobra.github.io/cobratoolbox/docs/ExcelModelFileDefinition.html for the current specifications.']);
end

if ~all(ismember(requiredMetHeaders,MetStrings(1,:)))
    error(['Required Headers not present in the "Metabolite List" sheet of the provided xls file.', sprintf('\n'), ...
           'Note, that headers are case sesnitive!', sprintf('\n'),...
           'Another likely source for this issue is a change in the xls format specification.', sprintf('\n'),...
           'Please have a look at the specification at https://opencobra.github.io/cobratoolbox/docs/ExcelModelFileDefinition.html for the current specifications.']);
end

rxnHeaders = rxnInfo(1,:);

for n = 1:length(rxnHeaders)
    if isnan(rxnHeaders{n})
        rxnHeaders{n} = '';
    end
end

% Assuming first row is header row
rxnAbrList = Strings(2:end,strmatch('Abbreviation',rxnHeaders,'exact'));
if ~isempty(strmatch('Description',rxnHeaders,'exact'))
    rxnNameList = Strings(2:end,strmatch('Description',rxnHeaders,'exact'));
else
    rxnNameList = Strings(2:end,strmatch('Abbreviation',rxnHeaders,'exact'));
end
rxnList = Strings(2:end,strmatch('Reaction',rxnHeaders,'exact'));
if ~isempty(strmatch('GPR',rxnHeaders,'exact'))
    grRuleList = Strings(2:end,strmatch('GPR',rxnHeaders,'exact'));
else
    grRuleList = cell(size(rxnList,1),1);
    grRuleList(:) = {''};
end

if ~isempty(strmatch('Proteins',rxnHeaders,'exact'))
    Protein = Strings(2:end,strmatch('Proteins',rxnHeaders,'exact'));
end

if ~isempty(strmatch('Subsystem',rxnHeaders,'exact'))
    subSystemList = Strings(2:end,strmatch('Subsystem',rxnHeaders,'exact'));
    subSystemList = cellfun(@(x) strsplit(x,';'), subSystemList ,'UniformOutput',0);
else
    subSystemList = cell(size(rxnList,1),1);
    subSystemList(:) = {{''}};
end

% initialization with default values
lowerBoundList = -defaultbound*ones(length(rxnAbrList),1);

if ~isempty(strmatch('Lower bound',rxnHeaders,'exact'))
    tmp = rxnInfo(2:end,strmatch('Lower bound',rxnHeaders,'exact'));
    for i = 1:length(tmp)
        if isnumeric(tmp{i})
            lowerBoundList(i) = tmp{i};
        else
            lowerBoundList(i) = str2num(tmp{i});
        end
    end
    lowerBoundList = columnVector(lowerBoundList); %Default -1000
    lowerBoundList(isnan(lowerBoundList)) = -defaultbound;
end

% initialization with default values
upperBoundList = defaultbound*ones(length(rxnAbrList),1);

if ~isempty(strmatch('Upper bound',rxnHeaders,'exact'))
    tmp = rxnInfo(2:end,strmatch('Upper bound',rxnHeaders,'exact'));
    for i = 1:length(tmp)
        if isnumeric(tmp{i})
            upperBoundList(i) = tmp{i};
        else
            upperBoundList(i) = str2num(tmp{i});
        end
    end
    upperBoundList = columnVector(upperBoundList); %Default 1000;
    upperBoundList(isnan(upperBoundList)) = defaultbound;
end

revFlagList = lowerBoundList<0;

% initialization with default values
Objective = zeros(length(rxnAbrList),1);

if ~isempty(strmatch('Objective',rxnHeaders,'exact'))
    tmp = rxnInfo(2:end,strmatch('Objective',rxnHeaders,'exact'));
    for i = 1:length(tmp)
        if isnumeric(tmp{i})
            Objective(i) = tmp{i};
        else
            Objective(i) = str2num(tmp{i});
        end
    end
    Objective = columnVector(Objective);
    Objective(isnan(Objective)) = 0;
end

model = createModel(rxnAbrList,rxnNameList,rxnList,revFlagList,lowerBoundList,upperBoundList,subSystemList,grRuleList);

if ~isempty(strmatch('Confidence Score',rxnHeaders,'exact'))
    confidenceScores = rxnInfo(2:end,strmatch('Confidence Score',rxnHeaders,'exact'));
    if any(~cellfun(@isnumeric, confidenceScores))
        %replace any non numeric elements by their numbers.
        confidenceScores = cellfun(@str2num,confidenceScores,'UniformOutput',0);
    end
    confidenceScores(cellfun(@isempty,confidenceScores)) = {NaN}; %Replace empty by nan.
    model.rxnConfidenceScores = cell2mat(confidenceScores);
    model.rxnConfidenceScores(isnan(model.rxnConfidenceScores)) = 0;
end
if ~isempty(strmatch('EC Number',rxnHeaders,'exact'))
    %This needs to be changed to the new annotation scheme and putting the
    %ECNumbers there.
    model.rxnECNumbers = Strings(2:end,strmatch('EC Number',rxnHeaders,'exact'));
end
if ~isempty(strmatch('Notes',rxnHeaders,'exact'))
    model.rxnNotes = Strings(2:end,strmatch('Notes',rxnHeaders,'exact'));
end
if ~isempty(strmatch('References',rxnHeaders,'exact'))
    model.rxnReferences = Strings(2:end,strmatch('References',rxnHeaders,'exact'));
    numbers = cellfun(@isnumeric ,model.rxnReferences);
    model.rxnReferences(numbers) = cellfun(@convertNumberToID , model.rxnReferences(numbers),'UniformOutput',0);
    model.rxnReferences = cellfun(@(x) regexprep(x,'PMID:',''), model.rxnReferences,'UniformOutput',0);
end

%fill in opt info for metabolites
if ~isempty(Objective) && length(Objective) == length(model.rxns)
    model.c = Objective;
end

metHeaders = metInfo(1,:);

for n = 1:length(metHeaders)
    if isnan(metHeaders{n})
        metHeaders{n} = '';
    end
end

% case 1: all metabolites in List have a compartment assignement

metCol = strmatch('Abbreviation',metHeaders,'exact');
Compartments = {};
mets = MetStrings(:,metCol);
%Now, we could have a problem, if the reactions are presented without
%compartments. In this instance, we would have to first put a "[c]" id
%behind all metabolites.
metCompAbbrev = cellfun(@(x) regexp(x,'.*\[(.*)\]$','tokens'), mets, 'UniformOutput', 0);
%get those which don't have a compartmentID
noncomps = cellfun(@isempty, metCompAbbrev);
mets(noncomps) = strcat(mets(noncomps),'[c]');

[A,B] = ismember(model.mets,mets);
matchingmets = mets(B(A));

if numel(matchingmets) ~= numel(model.mets)
    fprintf('The following metabolites from the reaction formulas did not have a matching metabolite in the metabolite list:\n');
    disp(setdiff(model.mets,mets));
    error('Not all metabolites could be matched');
end

if isempty(strmatch('Compartment',metHeaders,'exact'))
    %we use default compartments
    [compartmentAbbr,compartments] = getDefaultCompartmentSymbols();
    %lets check if all metabolites do have a standard compartment
    metCompAbbrev = cellfun(@(x) regexp(x,['.*\[(' strjoin(compartmentAbbr,'|') '\]$'],'tokens'), matchingmets, 'UniformOutput', 0);
    noncomps = cellfun(@isempty, metCompAbbrev);
    if any(noncomps)
        %So, there are missing compartment ids.
        %lets move all those metabolites to the cytosol, checking, that we
        %don't generate replicates.
        matchingmets(noncomps) = strcat(matchingmets(noncomps),'[c]');
        if numel(unique(matchingmets)) ~= numel(matchingmets)
            [~,ia] = unique(matchingmets);
            non_unique = matchingmets(setdiff(1:numel(matchinmets),ia));
            disp(unique(non_unique))
            error(['The above metabolites are present both without compartment identifier and with id in the cytosol.\n', ...
                  'Metabolites without compartment id are assumed to be located in the cytosol, and these metabolites would lead to duplicate metabolite ids!']);
        end
    end
    %Now, there should be no metabolites without compartment.
    %lets collect the compartments.
    Comps = cellfun(@(x) x{1} ,cellfun(@(x) regexp(x,['.*\[([' strjoin(compartmentAbbr,'') '])\]$'],'tokens'), matchingmets));
    %matchingmets has already the right order.
    model.comps = columnVector(compartmentAbbr(ismember(compartmentAbbr,Comps)));
    model.compNames = columnVector(compartments(ismember(compartmentAbbr,Comps)));
    model.mets = columnVector(matchingmets);
else
    %if Compartments is present, we will create a translation table
    %(ignoring everything that is empty)
    Compartments = MetStrings(B(A),strmatch('Compartment',metHeaders,'exact'));
    Cytosolname = 'cytosol';
    metCompAbbrev = cellfun(@(x) regexp(x,['.*\[(.*)\]$'],'tokens'), matchingmets, 'UniformOutput', 0);
    noncomps = cellfun(@isempty, metCompAbbrev);
    CytoNames = setdiff(unique(Compartments(~cellfun(@isempty, cellfun(@(x) regexp(x,'.*\[(c)\]$','tokens'), matchingmets, 'UniformOutput', 0)))),'');
    if numel(CytoNames) == 1
        Cytosolname = CytoNames{1};
    else
        CytoNames{end+1} = Cytosolname;
        CytoNames = unique(CytoNames);
    end

    if any(noncomps)
        matchingmets(noncomps) = strcat(matchingmets(noncomps),'[c]');
        Compartments(noncomps) = {Cytosolname};
        if numel(unique(matchingmets)) ~= numel(matchingmets)
            [~,ia] = unique(matchingmets);
            non_unique = matchingmets(setdiff(1:numel(matchinmets),ia));
            disp(unique(non_unique))
            error(['The above metabolites are present both without compartment identifier and with id in the cytosol.\n',...
                  'Metabolites without compartment id are assumed to be located in the cytosol, and these metabolites would lead to duplicate metabolite ids!']);
        end
    end
    metCompAbbrev = cellfun(@(x) x{1}, cellfun(@(x) regexp(x,['.*\[(.*)\]$'],'tokens'), matchingmets),'UniformOutput',false);

    %now reorder them and assign names to the ids.
    [ucomps, origpos] = unique(Compartments);
    [model.comps,~,origin] = unique(metCompAbbrev(origpos));
    %Column Vector
    model.comps = columnVector(model.comps);
    if ischar(model.comps)
        model.comps = cellstr(model.comps);
    end
    for i = 1:numel(model.comps)
        %combine all, ignoring empty entries.
        CompNames{i} = strjoin(setdiff(ucomps(origin==i),''),' or ');
    end
    model.compNames = columnVector(CompNames);
    model.mets = columnVector(matchingmets);
end
%%Set metNames
if ~isempty(strmatch('Description',metHeaders,'exact'))
    model.metNames = columnVector(MetStrings(B(A),strmatch('Description',metHeaders,'exact')));
end
%%Set Formulas
if ~isempty(strmatch('Charged formula',metHeaders,'exact'))
    model.metFormulas = columnVector(MetStrings(B(A),strmatch('Charged formula',metHeaders,'exact')));
end
if ~isempty(strmatch('Formula',metHeaders,'exact'))
    model.metFormulas = columnVector(MetStrings(B(A),strmatch('Formula',metHeaders,'exact')));
end
%%Set Charge
if ~isempty(strmatch('Charge',metHeaders,'exact'))
    model.metCharges = cell2mat(columnVector(metInfo(B(A),strmatch('Charge',metHeaders,'exact'))));
end

if ~isempty(strmatch('SMILES',metHeaders,'exact'))
    model.metSmiles= columnVector(MetStrings(B(A),strmatch('SMILES',metHeaders,'exact')));
end

%% Set annotations. (Has to be updated, once annotation structure is defined)
if ~isempty(strmatch('KEGG ID',metHeaders,'exact'))
    model.metKEGGID = columnVector(MetStrings(B(A),strmatch('KEGG ID',metHeaders,'exact')));
end
if ~isempty(strmatch('InChI string',metHeaders,'exact'))
    model.metInChIString = columnVector(MetStrings(B(A),strmatch('InChI string',metHeaders,'exact')));
end
if ~isempty(strmatch('HMDB ID',metHeaders,'exact'))
    model.metHMDBID = columnVector(MetStrings(B(A),strmatch('HMDB ID',metHeaders,'exact')));
end

if ~isempty(strmatch('PubChem ID',metHeaders,'exact'))
    %This is a litte trickier, as PubChemIDs are numbers. So we have to
    %load them differently
    model.metPubChemID = columnVector(metInfo(B(A),strmatch('PubChem ID',metHeaders,'exact')));
    numbers = cellfun(@isnumeric ,model.metPubChemID);
    model.metPubChemID(numbers) = cellfun(@convertNumberToID , model.metPubChemID(numbers),'UniformOutput',0);
end
if ~isempty(strmatch('ChEBI ID',metHeaders,'exact'))
    model.metChEBIID  = columnVector(metInfo(B(A),strmatch('ChEBI ID',metHeaders,'exact')));
    numbers = cellfun(@isnumeric ,model.metChEBIID);
    model.metChEBIID(numbers) = cellfun(@convertNumberToID , model.metChEBIID(numbers),'UniformOutput',0);
end

[~,fileName,extension] = fileparts(fileName);

model.description = [fileName, extension];

warning on
end

function stringNumber = convertNumberToID(number)
if isnan(number)
    stringNumber = '';
else
    stringNumber = num2str(number);
end
end


function layers = spp_layers_in_gpu(layers)
% layers = spp_layers_in_gpu(layers)
%   create each layers weight array in gpu
%
% AUTORIGHTS
% ---------------------------------------------------------
% Copyright (c) 2014, Shaoqing Ren
%
% This file is part of the SPP code and is available
% under the terms of the Simplified BSD License provided in
% LICENSE. Please retain this notice and LICENSE if you use
% this file (or any portion of it) in your project.
% ---------------------------------------------------------

    for i = 1:length(layers)
       if isempty(layers(i).weights)
          continue;
       end
       layers(i).weights_gpu = layers(i).weights;
       for j = 1:length(layers(i).weights)
            layers(i).weights_gpu{j} = gpuArray(layers(i).weights{j});
       end
    end
end


clf; figure(1);

subplot('position',[0 0 1 1]);
imshow(0);

subplot('position',[0.5 0 1 1]);
imshow(1);


function chanMaps = loadChanMaps()

ksroot = fileparts(fileparts(mfilename('fullpath')));
chanMapFiles = dir(fullfile(ksroot, 'configFiles', '*.mat'));

idx = 1;
chanMaps = [];
orig_state = warning;
warning('off','all')

for c = 1:numel(chanMapFiles)

    q = load(fullfile(ksroot, 'configFiles', chanMapFiles(c).name));

    cm = createValidChanMap(q, chanMapFiles(c).name);
    if ~isempty(cm)
        if idx==1; chanMaps = cm; else; chanMaps(idx) = cm; end
        idx = idx+1;
    end

end

warning(orig_state);


function xyz_write ( output_filename, point_num, xyz )

%*****************************************************************************80
%
%% XY_WRITE writes an XY file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 January 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string OUTPUT_FILENAME, the name of the file
%    to which the data should be written.
%
%    Input, integer POINT_NUM, the number of points.
%
%    Input, real XYZ(3,POINT_NUM), the point coordinates.
%

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

  if ( output_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'XYZ_WRITE - Error!\n' );
    fprintf ( 1, '  Could not open the output file "%s".\n', output_filename );
    error ( 'XYZ_WRITE - Error!' );
    return;
  end
%
%  Write the header.
%
  xyz_header_write ( output_filename, output_unit, point_num );
%
%  Write the data.
%
  xyz_data_write ( output_unit, point_num, xyz );
%
%  Close the file.
%
  fclose ( output_unit );

  return
end


function [y,fs,hd,ffx]=readsfs(ff,ty,sub,mode,nmax,nskip,xpath)
%READSFS  Read a .SFS format sound file [Y,FS,HD,FFX]=(FF,TY,SUB,MODE,NMAX,NSKIP,XPATH)
%
% Usage:  [s,fs]=readsfs(filename,1); % read the last speech item in a file
%
% Input Parameters:
%
%  FF gives the name of the file or alternatively
%                 can be the ffx output from a previous call to readsfs
%  TY gives the type of data item required e.g.:
%              0 Main header, 1 Speech data, 2 Laryngograph, 5 Annotation
%  SUB specifies the instance of type TY in the file: 0 for first (default), -1 for last or else
%      it can specify the start of the processing history field as a string (e.g. 'hqtx')
%  MODE		specifies the following (*=default):
%
%           File I/O: 'f'    Do not close file on exit
%             'd'    Look in data directory: voicebox('dir_data')
%           Int Format: 'i'  Force integer data to be at least 16 bits
%                              (some sfs files have a header error which falsely indicates 8-bit data)
%           Create item: 'c' Create item if necessary
%
%  NMAX     maximum number of samples to read (or -1 for unlimited [default])
%  NSKIP    number of samples or frames to skip from start of file
%               (or -1 to continue from previous read when FFX is given instead of a filename [default])
%  XPATH    (used with 'c' option) gives the full name of the program needed to generate the data or
%           the directory containing it.
%
% Output Parameters:
%
%  Y        data matrix or cell matrix whose format depends on TY:
%        TY=0: empty
%			TY=5: cell array {nf,3} = {position length annotation}
%        TY=1,2: column vector containing data
%        TY=11: data array with one row per frame
%  FS       sample frequency in Hz
%  HD     cell matrix whose format depends on TY:
%        TY=0: cell{14,1}
%              {1} row vector
%                {1}(1) = serial_date (see DATENUM() for format)
%                {1}(2) = file_number
%                {1}(3) = machine_type
%              {2} = File type (= 'UC2')
%              {3} = username of creator
%              {4} = site of creator
%              {5} = source
%              {6} = database
%              {7} = speaker name
%              {8} = session code
%              {9} = session date (as a string)
%             {10} = name of token
%             {11} = token repetition code
%             {12} = recording conditions
%             {13} = archiving details
%             {14} = general comments
%        TY>0: cell{4,1}
%              {1} = (1,14) array:
%                 {1}(1)  = processdate (see DATENUM() for format)
%                 {1}(2)  = datatype: 1=speech, 2=lx, 3=tx cycle lengths, 4=fx freq
%                                     5=annotations, 6=phonetic, 7=synthesiser, 8=words
%                                     9=grey-level, 10=voicing, 11=energy, 12=formants
%                                     13=energy, 14=lpc, 15=markov, 16=acoustic, 17=?,
%                                     18=geometry, 19=aerodynamics, 20=articulatory
%                                     21=source, 22=physiological, 23=rational filter
%                                     24=poles/zeros, 25=glottal flow, 26=excitation model
%                                     27=nose, 28=calibration, 29=area
%                 {1}(3)  = subtype
%                 {1}(4)  = floating: 1=float, 0=int, -1=structure+
%                 {1}(5)  = datasize in bytes
%                 {1}(6)  = framesize in units of datasize
%                 {1}(7)  = numframes
%                 {1}(8)  = length in bytes of data
%                 {1}(9)  = frameduration=1/sample_rate
%                 {1}(10) = datapresent: 0=deleted, 1=present, 2=link
%                 {1}(11) = time offset
%                 {1}(12) = windowsize
%                 {1}(13) = overlap
%                 {1}(14) = lxsync
%              {2} = processing history
%              {3} = parameter field
%              {4} = comment
%
%  FFX     cell array containing:
%              {1} = filename
%              {2} = (1,4) = [fid byte_order item_row values_read]
%              {3} = (nitem,5) = one row per item [type subtype length position byteorder]
%              {4} = {nitem,3} = cell: one row per item {processing parameters comment} text strings
%
%
% The SFS (Speech Filing System) is a package mostly written by Mark Huckvale
% and is available for UNIX and PC systems from
% http://www.phon.ucl.ac.uk/resource/sfs/

% Features yet to be implemented:
%
%   (1) If no output parameters are specified, header information will be printed.
%   (2) following link items
%   (3) MODE options:
%                Scaling: 's'    Auto scale to make data peak = +-1
%                         'r'    Raw unscaled data (integer values)
%                         'q'    Scaled to make 0dBm0 be unity mean square
%                         'p' *	Scaled to make +-1 equal full scale
%                Errors   'r'    Return if file is non-existant

%	   Copyright (C) Mike Brookes 1998
%      Version: $Id: readsfs.m 1876 2012-05-25 15:43:29Z dmb $
%
%   VOICEBOX is a MATLAB toolbox for speech processing.
%   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   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 2 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 can obtain a copy of the GNU General Public License from
%   http://www.gnu.org/copyleft/gpl.html or by writing to
%   Free Software Foundation, Inc.,675 Mass Ave, Cambridge, MA 02139, USA.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if nargin<7
    xpath=voicebox('sfsbin');       % path for sfs programs
end
EXESUF=voicebox('sfssuffix');                                              % suffix for executable O/S dependent
if nargin<4 mode='p';
else mode = [mode(:).' 'p'];
end
if nargout==0
    if nargin<2 | ty<=0
        [yy,fs,hd,ffx]=readsfs(ff,0,0,mode);
        fprintf(1,'File: %s\n',ffx{1});
        fprintf(1,'Database: %s, Speaker: %s, Date: %s, Token: %s\n',hd{6},hd{7},hd{9},hd{10});
        lst=ffx{3};
        for it=2:size(lst,1);
            [yy,fs,hd,ffx]=readsfs(ffx,lst(it,1),lst(it,2),mode,0);
            nf=hd{1}(7);
            fd=hd{1}(9);
            fprintf(1,'%3d.%02d %ss @ %sHz (%d frames): %s\n',lst(it,1),lst(it,2),sprintsi(nf*fd),sprintsi(1/fd),nf,ffx{4}{it,1});
        end
    end
else
    it=[];
    xfid=[];                  % xfid will be non-empty second time around
    while (isempty(it))                 % may go round this loop twice
        if ischar(ff)           % If ff is a string we must read file
            if any(mode=='d')
                ff=fullfile(voicebox('dir_data'),ff);
            end
            fid=fopen(ff,'rb','b');
            if fid == -1 error(sprintf('Can''t open %s for input',ff)); end

            t=fread(fid,512,'uchar').';
            if (t(1:3)~='UC2')
                error(sprintf('%s is not an SFS file type UC2',ff))
            end
            itemlist = [0 1 0 0 t(512)];

            proglist={};
            for i=2:200
                pos = ftell(fid);
                [t,n]=fread(fid,512,'uchar');
                if (n < 512) break; end
                mm=pow2(1,8*([0 1 2 3]+(t(512)==0)*[3 1 -1 -3]));
                itemlist(i,:)=[mm*[t(389:392) t(393:396) t(413:416)] pos t(512)];
                if itemlist(i,1)>29
                    error(sprintf('%d is not a valid SFS item type',itemlist(i,1)))
                end
                proglist{i,1}=char(zerotrim(t(1:256)'));
                proglist{i,2}=char(zerotrim(t(257:384)'));
                proglist{i,3}=char(zerotrim(t(437:456)'));
                fseek(fid,itemlist(i,3),'cof');
            end
            ffx={ff; [fid 0 0 0]; itemlist; proglist};
        else
            ffx=ff;
            ff=ffx{1};
            fid=ffx{2}(1);
            if fid<0
                fid=fopen(ffx{1},'rb',char('b'+(ffx{2}(2)~=0)*('l'-'b')));
            end
        end

        % now try to find the requested item

        list=ffx{3};
        if nargin<2 ty=0; end
        if nargin<3 sub=0; end
        if ty<0 ty=list(1,1); end
        if ischar(sub)
            lsub=length(sub);
            proglist=ffx{4};

            for itt=size(proglist,1):-1:2
                if list(itt,1)==ty & length(proglist{itt,1})>=lsub
                    if strcmpi(sub,proglist{itt,1}(1:lsub))
                        it=itt;
                    end
                end
            end
            if (isempty(it))
                if any(mode=='c') & isempty(xfid)      % try to create item if we haven't tried before
                    xfid=-1;
                    if nargin>=7
                        xname=xpath;
                        xfid=fopen(xname);
                    end
                    if xfid<0
                        if any('/\'==xpath(end))        % would be better to use fullfile()
                            xname=[xpath sub EXESUF];
                        else
                            xname=[xpath '/' sub EXESUF];
                        end
                        xfid=fopen(xname);
                    end
                    if xfid<0
                        error(sprintf('Cannot find executable program %s',sub));
                    else
                        fclose(xfid);
                        fclose(fid); % close this file
                        doscom=['cmd /c "' xname '" ' ffx{1}];
                        %fprintf(1,'Executing: %s\n',doscom);
                        if dos(doscom) % run the program
                            error(sprintf('Error running DOS command: %s',doscom));
                        end
                        ff=ffx{1};          % force reread of header information
                    end
                else
                    error(sprintf('Cannot find item %d.%s in file: %s',ty,sub,ff));
                end
            end
        else % numeric subitem specification
            if sub>0
                it = find(list(:,1)==ty & list(:,2)==sub);
            elseif sub==0
                it = min(find(list(:,1)==ty));
            else
                it = max(find(list(:,1)==ty));
            end
            if (isempty(it))
                error(sprintf('Cannot find item %d.%d in file: %s',ty,sub,ff));
            end
        end
    end % loop up to two times while (isempty(it))
    lit=list(it,:);
    if ffx{2}(3)~=it
        ffx{2}(3)=it;
        ffx{2}(4)=0;
    end

    % read the selected item with the correct byte order

    if lit(5)~=ffx{2}(2)
        fclose(fid);
        fid=fopen(ffx{1},'rb',char('b'+(lit(5)~=0)*('l'-'b')));
        ffx{2}(1:2)=[fid lit(5)];
        if fid == -1 error(sprintf('Can''t open %s for input',ff)); end
    end
    fseek(fid,lit(4),'bof');

    y=[];
    fs=0;
    if ~lit(1)                              % read main header
        mb=fread(fid,512,'uchar').';
        if nargout>2
            mc=[1 4; 9 28; 29 32; 37 56; 57 76; 77 96; 97 116; 117 136; 137 296; 297 304; 305 312; 313 332; 333 412];
            hd=cell(14,1);
            hd{1}=[pow2(1,8*([2 3 0 1]+(mb(512)==0)*[1 -1 1 -1]))*[mb(5:8); mb(33:36)].' mb(512)];
            hd{1}(1)=hd{1}(1)/86400+719529;  % convert date format
            for i=1:13
                hd{i+1}=char(zerotrim(mb(mc(i,1):mc(i,2))));
            end
        end
    else

        % read the item header

        hd=cell(4,1);
        hdr=zeros(1,14);

        [str,n]=fread(fid,256,'uchar');
        if (n<256) error(sprintf('Error reading item %d.%d in file: %s',ty,sub,file)); end
        hd{2}=char(zerotrim(str'));

        [str,n]=fread(fid,128,'uchar');
        if (n<128) error(sprintf('Error reading item %d.%d in file: %s',ty,sub,file)); end
        hd{3}=char(zerotrim(str'));

        hdr(1:8)=fread(fid,8,'long');
        hdr(9)=fread(fid,1,'double');
        if hdr(9) fs=1/hdr(9); end
        hdr(10)=fread(fid,1,'long');
        hdr(11)=fread(fid,1,'double');

        [str,n]=fread(fid,20,'uchar');
        if (n<20) error(sprintf('Error reading item %d.%d in file: %s',ty,sub,file)); end
        hd{4}=char(zerotrim(str'));

        [hdr(12:14),n]=fread(fid,3,'long');
        if (n<3) error(sprintf('Error reading item %d.%d in file: %s',ty,sub,file)); end
        fseek(fid,44,'cof');
        hd{1}=hdr;
        hd{1}(1)=hd{1}(1)/86400+719529;  % convert date format

        % now read the actual data

        if nargin<6 nskip=ffx{2}(4);
        elseif nskip<0 nskip=ffx{2}(4);
        end

        ksamples=hdr(7)-nskip;
        if nargin>4
            if nmax>=0
                ksamples=min(nmax,ksamples);
            end
        end

        if ksamples>0
            ffx{2}(4)=nskip+ksamples;
            fsz=hdr(6);
            if(hdr(10)==1)		% data present
                if(hdr(4)>=0)		% non-structured
                    ds=hdr(5);
                    if(hdr(4)>0)
                        if(ds==4) fmt='float';
                        elseif (ds==8) fmt='double';
                        else error('error in sfs file'); end
                    else
                        if(ds==1 & all(mode~='i')) fmt='uchar';
                        elseif(ds<=2) fmt='short'; fsz=ceil(fsz*ds/2);
                        elseif(ds==4) fmt='long';
                        else error('error in sfs file'); end
                    end
                    fseek(fid,lit(4)+512+nskip*fsz*ds,'bof');
                    nd=fsz*ksamples;
                    [y,n]=fread(fid,nd,fmt);
                    if (n<nd) error(sprintf('Error reading item %d.%d in file: %s',ty,sub,file)); end
                    y = reshape(y,fsz,ksamples)';
                else
                    if (hdr(2)==5)
                        y = cell(ksamples,3);
                        for ifr=1:nskip
                            lf=fread(fid,1,'uchar');
                            fseek(fid,lf,'cof');
                        end

                        for ifr=1:ksamples
                            lf=fread(fid,1,'uchar');
                            tdat=fread(fid,2,'long');
                            y(ifr,:)={tdat(1) tdat(2) char(fread(fid,lf-9,'uchar').')};
                            lf=fread(fid,1,'uchar');
                        end
                    else
                        error(sprintf('Cannot convert item %d.%d in file: %s',ty,sub,file));
                    end
                end
            end
        end
    end
    if all(mode~='f') fclose(fid); ffx{2}(1)=-1; end
end


function VO = spm_write_sn(V,prm,flags,extras)
% Write out warped images
% FORMAT VO = spm_write_sn(V,prm,flags,msk)
% V         - Images to transform (filenames or volume structure).
% prm       - Transformation information (filename or structure).
% flags     - flags structure, with fields...
%           interp   - interpolation method (0-7)
%           wrap     - wrap edges (e.g., [1 1 0] for 2D MRI sequences)
%           vox      - voxel sizes (3 element vector - in mm)
%                      Non-finite values mean use template vox.
%           bb       - bounding box (2x3 matrix - in mm)
%                      Non-finite values mean use template bb.
%           preserve - either 0 or 1.  A value of 1 will "modulate"
%                      the spatially normalised images so that total
%                      units are preserved, rather than just
%                      concentrations.
%           prefix   - Prefix for normalised images. Defaults to 'w'.
% msk       - An optional cell array for masking the spatially
%             normalised images (see below).
%
% Warped images are written prefixed by "w".
%
% Non-finite vox or bounding box suggests that values should be derived
% from the template image.
%
% Don't use interpolation methods greater than one for data containing
% NaNs.
%__________________________________________________________________________
%
% FORMAT msk = spm_write_sn(V,prm,flags,'mask')
% V          - Images to transform (filenames or volume structure).
% prm        - Transformation information (filename or structure).
% flags      - flags structure, with fields...
%            wrap - wrap edges (e.g., [1 1 0] for 2D MRI sequences)
%            vox  - voxel sizes (3 element vector - in mm)
%                   Non-finite values mean use template vox.
%            bb   - bounding box (2x3 matrix - in mm)
%                   Non-finite values mean use template bb.
% msk        - a cell array for masking a series of spatially normalised
%              images.
%
%
%_________________________________________________________________________
%
% FORMAT VO = spm_write_sn(V,prm,'modulate')
% V         - Spatially normalised images to modulate (filenames or
%             volume structure).
% prm       - Transformation information (filename or structure).
%
%  After nonlinear spatial normalization, the relative volumes of some
%  brain structures will have decreased, whereas others will increase.
%  The resampling of the images preserves the concentration of pixel
%  units in the images, so the total counts from structures that have
%  reduced volumes after spatial normalization will be reduced by an
%  amount proportional to the volume reduction.
%
%  This routine rescales images after spatial normalization, so that
%  the total counts from any structure are preserved.  It was written
%  as an optional step in performing voxel based morphometry.
%
%__________________________________________________________________________
% Copyright (C) 1996-2011 Wellcome Trust Centre for Neuroimaging

% John Ashburner
% $Id: spm_write_sn.m 4878 2012-08-31 12:07:30Z guillaume $

SVNid = '$Rev: 4878 $';

%-Say hello
%--------------------------------------------------------------------------
SPMid = spm('FnBanner',mfilename,SVNid);

%-Get images
%--------------------------------------------------------------------------
if isempty(V), return; end

if ischar(prm), prm = load(prm);  end
if ischar(V),   V   = spm_vol(V); end

%-Modulate
%--------------------------------------------------------------------------
if nargin==3 && ischar(flags) && strcmpi(flags,'modulate')
    if nargout==0
        modulate(V,prm);
    else
        VO = modulate(V,prm);
    end
    return;
end

%-Get parameters
%--------------------------------------------------------------------------
def_flags        = spm_get_defaults('old.normalise.write');

if nargin < 3
    flags = def_flags;
else
    fnms = fieldnames(def_flags);
    for i=1:length(fnms)
        if ~isfield(flags,fnms{i})
            flags.(fnms{i}) = def_flags.(fnms{i});
        end
    end
end

%-Mask
%--------------------------------------------------------------------------
[x,y,z,mat] = get_xyzmat(prm,flags.bb,flags.vox);

if nargin==4
    if ischar(extras) && strcmpi(extras,'mask')
        VO = get_snmask(V,prm,x,y,z,flags.wrap);
        return;
    end
    if iscell(extras)
        msk = extras;
    end
end

if nargout>0 && numel(V)>8
    error('Too many images to save in memory');
end

%-Warp images
%--------------------------------------------------------------------------
if ~exist('msk','var')
    msk = get_snmask(V,prm,x,y,z,flags.wrap);
end

if nargout==0
    if isempty(prm.Tr)
        affine_transform(V,prm,x,y,z,mat,flags,msk);
    else
        nonlin_transform(V,prm,x,y,z,mat,flags,msk);
    end
else
    if isempty(prm.Tr)
        VO = affine_transform(V,prm,x,y,z,mat,flags,msk);
    else
        VO = nonlin_transform(V,prm,x,y,z,mat,flags,msk);
    end
end

fprintf('%-40s: %30s\n','Completed',spm('time'))                        %-#

%==========================================================================
%-function VO = affine_transform(V,prm,x,y,z,mat,flags,msk)
%==========================================================================
function VO = affine_transform(V,prm,x,y,z,mat,flags,msk)

[X,Y] = ndgrid(x,y);
d     = [flags.interp*[1 1 1]' flags.wrap(:)];

spm_progress_bar('Init',numel(V),'Resampling','volumes/slices completed');
for i=1:numel(V)
    VO     = make_hdr_struct(V(i),x,y,z,mat, flags.prefix);
    if flags.preserve
        VO.fname = spm_file(VO.fname, 'prefix','m');
    end
    detAff = det(prm.VF(1).mat*prm.Affine/prm.VG(1).mat);
    if flags.preserve, VO.pinfo(1:2,:) = VO.pinfo(1:2,:)/detAff; end

    %Dat= zeros(VO.dim(1:3));
    Dat = single(0);
    Dat(VO.dim(1),VO.dim(2),VO.dim(3)) = 0;

    C = spm_bsplinc(V(i),d);

    for j=1:length(z)   % Cycle over planes
        [X2,Y2,Z2]  = mmult(X,Y,z(j),V(i).mat\prm.VF(1).mat*prm.Affine);
        dat         = spm_bsplins(C,X2,Y2,Z2,d);
        if flags.preserve, dat = dat*detAff; end
        dat(msk{j}) = NaN;

        Dat(:,:,j) = single(dat);

        if numel(V)<5, spm_progress_bar('Set',i-1+j/length(z)); end
    end
    if nargout~=0
        VO.pinfo  = [1 0]';
        VO.dt     = [spm_type('float32') spm_platform('bigend')];
        VO.dat    = Dat;
    else
        spm_write_vol(VO, Dat);
    end
    spm_progress_bar('Set',i);
end
spm_progress_bar('Clear');

%==========================================================================
%-function VO = nonlin_transform(V,prm,x,y,z,mat,flags,msk)
%==========================================================================
function VO = nonlin_transform(V,prm,x,y,z,mat,flags,msk)

[X,Y] = ndgrid(x,y);
Tr = prm.Tr;
BX = spm_dctmtx(prm.VG(1).dim(1),size(Tr,1),x-1);
BY = spm_dctmtx(prm.VG(1).dim(2),size(Tr,2),y-1);
BZ = spm_dctmtx(prm.VG(1).dim(3),size(Tr,3),z-1);
if flags.preserve
    DX = spm_dctmtx(prm.VG(1).dim(1),size(Tr,1),x-1,'diff');
    DY = spm_dctmtx(prm.VG(1).dim(2),size(Tr,2),y-1,'diff');
    DZ = spm_dctmtx(prm.VG(1).dim(3),size(Tr,3),z-1,'diff');
end
d  = [flags.interp*[1 1 1]' flags.wrap(:)];

spm_progress_bar('Init',numel(V),'Resampling','volumes completed');
for i=1:numel(V)
    VO     = make_hdr_struct(V(i),x,y,z,mat, flags.prefix);
    if flags.preserve
        VO.fname = spm_file(VO.fname, 'prefix','m');
    end
    detAff = det(prm.VF(1).mat*prm.Affine/prm.VG(1).mat);

    % Accumulate data
    %Dat= zeros(VO.dim(1:3));
    Dat = single(0);
    Dat(VO.dim(1),VO.dim(2),VO.dim(3)) = 0;

    C = spm_bsplinc(V(i),d);

    for j=1:length(z)   % Cycle over planes
        % Nonlinear deformations
        %------------------------------------------------------------------
        tx = get_2Dtrans(Tr(:,:,:,1),BZ,j);
        ty = get_2Dtrans(Tr(:,:,:,2),BZ,j);
        tz = get_2Dtrans(Tr(:,:,:,3),BZ,j);
        X1 = X    + BX*tx*BY';
        Y1 = Y    + BX*ty*BY';
        Z1 = z(j) + BX*tz*BY';

        [X2,Y2,Z2]  = mmult(X1,Y1,Z1,V(i).mat\prm.VF(1).mat*prm.Affine);
        dat         = spm_bsplins(C,X2,Y2,Z2,d);
        dat(msk{j}) = NaN;

        if ~flags.preserve
            Dat(:,:,j) = single(dat);
        else
            j11 = DX*tx*BY' + 1; j12 = BX*tx*DY';     j13 = BX*get_2Dtrans(Tr(:,:,:,1),DZ,j)*BY';
            j21 = DX*ty*BY';     j22 = BX*ty*DY' + 1; j23 = BX*get_2Dtrans(Tr(:,:,:,2),DZ,j)*BY';
            j31 = DX*tz*BY';     j32 = BX*tz*DY';     j33 = BX*get_2Dtrans(Tr(:,:,:,3),DZ,j)*BY' + 1;

            % The determinant of the Jacobian reflects relative volume changes
            %-----------------------------------------------------------------
            dat       = dat .* (j11.*(j22.*j33-j23.*j32) - j21.*(j12.*j33-j13.*j32) + j31.*(j12.*j23-j13.*j22)) * detAff;
            Dat(:,:,j) = single(dat);
        end
        if numel(V)<5, spm_progress_bar('Set',i-1+j/length(z)); end
    end
    if nargout==0
        if flags.preserve, VO = rmfield(VO,'pinfo'); end
        VO = spm_write_vol(VO,Dat);
    else
        VO.pinfo  = [1 0]';
        VO.dt     = [spm_type('float32') spm_platform('bigend')];
        VO.dat    = Dat;
    end
    spm_progress_bar('Set',i);
end
spm_progress_bar('Clear');

%==========================================================================
%-function VO = modulate(V,prm)
%==========================================================================
function VO = modulate(V,prm)

spm_progress_bar('Init',numel(V),'Modulating','volumes completed');
for i=1:numel(V)
    VO          = V(i);
    VO          = rmfield(VO,'pinfo');
    VO.fname    = spm_file(VO.fname, 'prefix','m');
    detAff      = det(prm.VF(1).mat*prm.Affine/prm.VG(1).mat);
    %Dat        = zeros(VO.dim(1:3));
    Dat         = single(0);
    Dat(VO.dim(1),VO.dim(2),VO.dim(3)) = 0;
    [x,y,z,mat] = get_xyzmat(prm,NaN,NaN,VO);

    if sum((mat(:)-VO.mat(:)).^2)>1e-7, error('Orientations not compatible'); end

    Tr = prm.Tr;

    if isempty(Tr)
        for j=1:length(z),   % Cycle over planes
            dat        = spm_slice_vol(V(i),spm_matrix([0 0 j]),V(i).dim(1:2),0);
            Dat(:,:,j) = single(dat);
            if numel(V)<5, spm_progress_bar('Set',i-1+j/length(z)); end
        end
    else
        BX = spm_dctmtx(prm.VG(1).dim(1),size(Tr,1),x-1);
        BY = spm_dctmtx(prm.VG(1).dim(2),size(Tr,2),y-1);
        BZ = spm_dctmtx(prm.VG(1).dim(3),size(Tr,3),z-1);
        DX = spm_dctmtx(prm.VG(1).dim(1),size(Tr,1),x-1,'diff');
        DY = spm_dctmtx(prm.VG(1).dim(2),size(Tr,2),y-1,'diff');
        DZ = spm_dctmtx(prm.VG(1).dim(3),size(Tr,3),z-1,'diff');

        for j=1:length(z)   % Cycle over planes

            tx = get_2Dtrans(Tr(:,:,:,1),BZ,j);
            ty = get_2Dtrans(Tr(:,:,:,2),BZ,j);
            tz = get_2Dtrans(Tr(:,:,:,3),BZ,j);

            j11 = DX*tx*BY' + 1; j12 = BX*tx*DY';     j13 = BX*get_2Dtrans(Tr(:,:,:,1),DZ,j)*BY';
            j21 = DX*ty*BY';     j22 = BX*ty*DY' + 1; j23 = BX*get_2Dtrans(Tr(:,:,:,2),DZ,j)*BY';
            j31 = DX*tz*BY';     j32 = BX*tz*DY';     j33 = BX*get_2Dtrans(Tr(:,:,:,3),DZ,j)*BY' + 1;

            % The determinant of the Jacobian reflects relative volume changes
            %-----------------------------------------------------------------
            dat        = spm_slice_vol(V(i),spm_matrix([0 0 j]),V(i).dim(1:2),0);
            dat        = dat .* (j11.*(j22.*j33-j23.*j32) - j21.*(j12.*j33-j13.*j32) + j31.*(j12.*j23-j13.*j22)) * detAff;
            Dat(:,:,j) = single(dat);

            if numel(V)<5, spm_progress_bar('Set',i-1+j/length(z)); end
        end
    end

    if nargout==0
        VO = spm_write_vol(VO,Dat);
    else
        VO.pinfo  = [1 0]';
        VO.dt     = [spm_type('float32') spm_platform('bigend')];
        VO.dat    = Dat;
    end
    spm_progress_bar('Set',i);
end
spm_progress_bar('Clear');

%==========================================================================
%-function VO   = make_hdr_struct(V,x,y,z,mat,prefix)
%==========================================================================
function VO   = make_hdr_struct(V,x,y,z,mat,prefix)
VO            = V;
VO.fname      = spm_file(V.fname, 'prefix',prefix);
VO.mat        = mat;
VO.dim(1:3)   = [length(x) length(y) length(z)];
VO.pinfo      = V.pinfo;
VO.descrip    = 'spm - 3D normalized';

%==========================================================================
%-function T2 = get_2Dtrans(T3,B,j)
%==========================================================================
function T2 = get_2Dtrans(T3,B,j)
d   = [size(T3) 1 1 1];
tmp = reshape(T3,d(1)*d(2),d(3));
T2  = reshape(tmp*B(j,:)',d(1),d(2));

%==========================================================================
%-function Mask = getmask(X,Y,Z,dim,wrp)
%==========================================================================
function Mask = getmask(X,Y,Z,dim,wrp)
% Find range of slice
tiny = 5e-2;
Mask = true(size(X));
if ~wrp(1), Mask = Mask & (X >= (1-tiny) & X <= (dim(1)+tiny)); end
if ~wrp(2), Mask = Mask & (Y >= (1-tiny) & Y <= (dim(2)+tiny)); end
if ~wrp(3), Mask = Mask & (Z >= (1-tiny) & Z <= (dim(3)+tiny)); end

%==========================================================================
%-function [X2,Y2,Z2] = mmult(X1,Y1,Z1,Mult)
%==========================================================================
function [X2,Y2,Z2] = mmult(X1,Y1,Z1,Mult)
if length(Z1) == 1
    X2= Mult(1,1)*X1 + Mult(1,2)*Y1 + (Mult(1,3)*Z1 + Mult(1,4));
    Y2= Mult(2,1)*X1 + Mult(2,2)*Y1 + (Mult(2,3)*Z1 + Mult(2,4));
    Z2= Mult(3,1)*X1 + Mult(3,2)*Y1 + (Mult(3,3)*Z1 + Mult(3,4));
else
    X2= Mult(1,1)*X1 + Mult(1,2)*Y1 + Mult(1,3)*Z1 + Mult(1,4);
    Y2= Mult(2,1)*X1 + Mult(2,2)*Y1 + Mult(2,3)*Z1 + Mult(2,4);
    Z2= Mult(3,1)*X1 + Mult(3,2)*Y1 + Mult(3,3)*Z1 + Mult(3,4);
end

%==========================================================================
%-function msk = get_snmask(V,prm,x,y,z,wrap)
%==========================================================================
function msk = get_snmask(V,prm,x,y,z,wrap)
% Generate a mask for where there is data for all images
%--------------------------------------------------------------------------
msk = cell(length(z),1);
t1 = cat(3,V.mat);
t2 = cat(1,V.dim);
t  = [reshape(t1,[16 length(V)])' t2(:,1:3)];
Tr = prm.Tr;
[X,Y] = ndgrid(x,y);

BX = spm_dctmtx(prm.VG(1).dim(1),size(Tr,1),x-1);
BY = spm_dctmtx(prm.VG(1).dim(2),size(Tr,2),y-1);
BZ = spm_dctmtx(prm.VG(1).dim(3),size(Tr,3),z-1);

if numel(V)>1 && any(any(diff(t,1,1)))
    spm_progress_bar('Init',length(z),'Computing available voxels','planes completed');
    for j=1:length(z)   % Cycle over planes
        Count = zeros(length(x),length(y));
        if isempty(Tr)
            % Generate a mask for where there is data for all images
            %--------------------------------------------------------------
            for i=1:numel(V),
                [X2,Y2,Z2] = mmult(X,Y,z(j),V(i).mat\prm.VF(1).mat*prm.Affine);
                Count      = Count + getmask(X2,Y2,Z2,V(i).dim(1:3),wrap);
            end
        else
            % Nonlinear deformations
            %--------------------------------------------------------------
            X1 = X    + BX*get_2Dtrans(Tr(:,:,:,1),BZ,j)*BY';
            Y1 = Y    + BX*get_2Dtrans(Tr(:,:,:,2),BZ,j)*BY';
            Z1 = z(j) + BX*get_2Dtrans(Tr(:,:,:,3),BZ,j)*BY';

            % Generate a mask for where there is data for all images
            %--------------------------------------------------------------
            for i=1:numel(V)
                [X2,Y2,Z2] = mmult(X1,Y1,Z1,V(i).mat\prm.VF(1).mat*prm.Affine);
                Count      = Count + getmask(X2,Y2,Z2,V(i).dim(1:3),wrap);
            end
        end
        msk{j} = uint32(find(Count ~= numel(V)));
        spm_progress_bar('Set',j);
    end
    spm_progress_bar('Clear');
else
    for j=1:length(z), msk{j} = uint32([]); end
end

%==========================================================================
%-function [x,y,z,mat] = get_xyzmat(prm,bb,vox,VG)
%==========================================================================
function [x,y,z,mat] = get_xyzmat(prm,bb,vox,VG)
% The old voxel size and origin notation is used here.
% This requires that the position and orientation
% of the template is transverse.  It would not be
% straitforward to account for templates that are
% in different orientations because the basis functions
% would no longer be seperable.  The seperable basis
% functions mean that computing the deformation field
% from the parameters is much faster.

% bb  = sort(bb);
% vox = abs(vox);

if nargin<4
    VG = prm.VG(1);

    if all(~isfinite(bb(:))) && all(~isfinite(vox(:)))
        x   = 1:VG.dim(1);
        y   = 1:VG.dim(2);
        z   = 1:VG.dim(3);
        mat = VG.mat;
        return;
    end
end

[bb0 vox0] = spm_get_bbox(VG, 'old');
if ~all(isfinite(vox(:))), vox = vox0; end
if ~all(isfinite(bb(:))),  bb  = bb0;  end

msk       = find(vox<0);
bb        = sort(bb);
bb(:,msk) = flipud(bb(:,msk));

% Adjust bounding box slightly - so it rounds to closest voxel.
% Comment out if not needed.
%bb(:,1) = round(bb(:,1)/vox(1))*vox(1);
%bb(:,2) = round(bb(:,2)/vox(2))*vox(2);
%bb(:,3) = round(bb(:,3)/vox(3))*vox(3);

M   = prm.VG(1).mat;
vxg = sqrt(sum(M(1:3,1:3).^2));
if det(M(1:3,1:3))<0, vxg(1) = -vxg(1); end
ogn = M\[0 0 0 1]';
ogn = ogn(1:3)';

% Convert range into range of voxels within template image
x   = (bb(1,1):vox(1):bb(2,1))/vxg(1) + ogn(1);
y   = (bb(1,2):vox(2):bb(2,2))/vxg(2) + ogn(2);
z   = (bb(1,3):vox(3):bb(2,3))/vxg(3) + ogn(3);

og  = -vxg.*ogn;

% Again, chose whether to round to closest voxel.
%of  = -vox.*(round(-bb(1,:)./vox)+1);
of = bb(1,:)-vox;

M1  = [vxg(1) 0 0 og(1) ; 0 vxg(2) 0 og(2) ; 0 0 vxg(3) og(3) ; 0 0 0 1];
M2  = [vox(1) 0 0 of(1) ; 0 vox(2) 0 of(2) ; 0 0 vox(3) of(3) ; 0 0 0 1];
mat = prm.VG(1).mat*inv(M1)*M2;

LEFTHANDED = true;
if (LEFTHANDED && det(mat(1:3,1:3))>0) || (~LEFTHANDED && det(mat(1:3,1:3))<0),
    Flp = [-1 0 0 (length(x)+1); 0 1 0 0; 0 0 1 0; 0 0 0 1];
    mat = mat*Flp;
    x   = flipud(x(:))';
end

%==========================================================================
%-function VO = write_dets(P,bb,vox)
%==========================================================================
function VO = write_dets(P,bb,vox)
if nargin==1
    job = P;
    P   = job.P;
    bb  = job.bb;
    vox = job.vox;
end

spm_progress_bar('Init',numel(P),'Writing','volumes completed');

for i=1:numel(V)
    prm = load(deblank(P{i}));
    [x,y,z,mat] = get_xyzmat(prm,bb,vox);
    Tr = prm.Tr;
    BX = spm_dctmtx(prm.VG(1).dim(1),size(Tr,1),x-1);
    BY = spm_dctmtx(prm.VG(1).dim(2),size(Tr,2),y-1);
    BZ = spm_dctmtx(prm.VG(1).dim(3),size(Tr,3),z-1);
    DX = spm_dctmtx(prm.VG(1).dim(1),size(Tr,1),x-1,'diff');
    DY = spm_dctmtx(prm.VG(1).dim(2),size(Tr,2),y-1,'diff');
    DZ = spm_dctmtx(prm.VG(1).dim(3),size(Tr,3),z-1,'diff');

    VO = struct(...
        'fname',   spm_file(P{i}, 'prefix','jy_'),...
        'dim',     [numel(x),numel(y),numel(z)],...
        'dt',      [spm_type('float32') spm_platform('bigend')],...
        'pinfo',   [1 0 0]',...
        'mat',     mat,...
        'n',       1,...
        'descrip', 'Jacobian determinants');
    VO     = spm_create_vol(VO);
    detAff = det(prm.VF(1).mat*prm.Affine/prm.VG(1).mat);
    Dat    = single(0);
    Dat(VO.dim(1),VO.dim(2),VO.dim(3)) = 0;

    for j=1:length(z)   % Cycle over planes
        % Nonlinear deformations
        tx = get_2Dtrans(Tr(:,:,:,1),BZ,j);
        ty = get_2Dtrans(Tr(:,:,:,2),BZ,j);
        tz = get_2Dtrans(Tr(:,:,:,3),BZ,j);

        %------------------------------------------------------------------
        j11 = DX*tx*BY' + 1; j12 = BX*tx*DY';     j13 = BX*get_2Dtrans(Tr(:,:,:,1),DZ,j)*BY';
        j21 = DX*ty*BY';     j22 = BX*ty*DY' + 1; j23 = BX*get_2Dtrans(Tr(:,:,:,2),DZ,j)*BY';
        j31 = DX*tz*BY';     j32 = BX*tz*DY';     j33 = BX*get_2Dtrans(Tr(:,:,:,3),DZ,j)*BY' + 1;
        % The determinant of the Jacobian reflects relative volume changes.
        %------------------------------------------------------------------
        dat       = (j11.*(j22.*j33-j23.*j32) - j21.*(j12.*j33-j13.*j32) + j31.*(j12.*j23-j13.*j22)) * detAff;
        Dat(:,:,j) = single(dat);
        if numel(P)<5, spm_progress_bar('Set',i-1+j/length(z)); end
    end
    VO = spm_write_vol(VO,Dat);
    spm_progress_bar('Set',i);
end
spm_progress_bar('Clear');


function info = gpuinfo()
%GPUINFO  get basic details about the GPU being used
%
%   GPUINFO() gets hold of some basic info about the GPU device being used.
%
%   See also: CPUINFO

%   Copyright 2011-2012 The MathWorks, Inc.

if parallel.gpu.GPUDevice.isAvailable()
    gpu = gpuDevice();
info = struct( ...
    'Name', gpu.Name, ...
    'Clock', sprintf( '%u MHz', gpu.ClockRateKHz/1e3 ), ...
    'NumProcessors', gpu.MultiprocessorCount, ...
    'ComputeCapability', gpu.ComputeCapability, ...
    'TotalMemory', sprintf( '%1.2f GB', gpu.TotalMemory/2^30 ), ...
    'CUDAVersion', gpu.DriverVersion, ...
    'DriverVersion', parallel.internal.gpu.CUDADriverVersion );
else
    % No GPU, so create an empty structure
info = struct( ...
    'Name', '<no GPU available>', ...
    'Clock', '', ...
    'NumProcessors', 0, ...
    'ComputeCapability', '', ...
    'TotalMemory', 0, ...
    'CUDAVersion', '', ...
    'DriverVersion', '' );
end


function bestEpoch = findBestEpoch(expDir, varargin)
%FINDBESTEPOCH finds the best epoch of training
%   FINDBESTEPOCH(EXPDIR) evaluates the checkpoints
%   (the `net-epoch-%d.mat` files created during
%   training) in EXPDIR
%
%   FINDBESTEPOCH(..., 'option', value, ...) accepts the following
%   options:
%
%   `priorityMetric`:: 'classError'
%    Determines the highest priority metric by which to rank the
%    checkpoints.
%
%   `prune`:: false
%    Removes all saved checkpoints to save space except:
%
%       1. The checkpoint with the lowest validation error metric
%       2. The last checkpoint
%
% Copyright (C) 2017 Samuel Albanie
% Licensed under The MIT License [see LICENSE.md for details]

  opts.prune = false ;
  opts.priorityMetric = 'classError' ;
  opts = vl_argparse(opts, varargin) ;

  lastEpoch = findLastCheckpoint(expDir);
  if ~lastEpoch, return ; end % return if no checkpoints were found

  bestEpoch = findBestValCheckpoint(expDir, opts.priorityMetric);
  if opts.prune
    preciousEpochs = [bestEpoch lastEpoch];
    removeOtherCheckpoints(expDir, preciousEpochs);
    fprintf('----------------------- \n');
    fprintf('directory cleaned: %s\n', expDir);
    fprintf('----------------------- \n');
  end

% -------------------------------------------------------------------------
function removeOtherCheckpoints(expDir, preciousEpochs)
% -------------------------------------------------------------------------
  list = dir(fullfile(expDir, 'net-epoch-*.mat')) ;
  tokens = regexp({list.name}, 'net-epoch-([\d]+).mat', 'tokens') ;
  epochs = cellfun(@(x) sscanf(x{1}{1}, '%d'), tokens) ;
  targets = ~ismember(epochs, preciousEpochs);
  files = cellfun(@(x) fullfile(expDir, sprintf('net-epoch-%d.mat', x)), ...
          num2cell(epochs(targets)), 'UniformOutput', false);
  cellfun(@(x) delete(x), files)

% -------------------------------------------------------------------------
function bestEpoch = findBestValCheckpoint(expDir, priorityMetric)
% -------------------------------------------------------------------------
  lastEpoch = findLastCheckpoint(expDir) ;
  if strcmp(priorityMetric, 'last'), bestEpoch = lastEpoch ; return ; end
  % handle the different storage structures/error metrics
  path = fullfile(expDir, sprintf('net-epoch-%d.mat', lastEpoch)) ;
  try
    data = load(path) ;
  catch
		msg = 'checkopint at %s was malformed, trying agin in 10 secs....\n' ;
		warning(msg, path) ; pause(10) ; data = load(path) ;
  end
  if isfield(data, 'stats')
    valStats = data.stats.val;
  elseif isfield(data, 'info')
    valStats = data.info.val;
  elseif isfield(data, 'state')
    valStats = data.state.stats.val ;
  else
    error('storage structure not recognised');
  end

  ascending = {'mAP', 'accuracy'} ;
  descending = {'top1error', 'error', 'mbox_loss', 'class_loss'} ;

  % find best checkpoint according to the following priority
  metrics = [{priorityMetric} ascending descending] ;

  for i = 1:numel(metrics)
    if isfield(valStats, metrics{i})
      errorMetric = [valStats.(metrics{i})] ;
      selectedMetric = metrics{i} ;
      break ;
    end
  end

  assert(logical(exist('errorMetric', 'var')), 'error metrics not recognized') ;
  if ismember(selectedMetric, ascending)
    pick = @max ;
  else
    pick = @min ;
  end

  [~, bestEpoch] = pick(errorMetric);

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