function DLHGui(command,varargin)
%function DLHGui(command,varargin)
%GUI for Dose Location Histogram (DLH)
%
%APA, 7/6/09
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

global planC stateS
indexS = planC{end};

if nargin==0
    command = 'INIT';
end

switch upper(command)

    case 'INIT'

        % define margin constraints
        leftMarginWidth = 300;
        topMarginHeight = 50;
        stateS.leftMarginWidth = leftMarginWidth;
        stateS.topMarginHeight = topMarginHeight;

        str1 = ['Dose Location Histogram'];
        position = [5 40 800 600];

        defaultColor = [0.8 0.9 0.9];

        if isempty(findobj('tag','DLHFig'))
            % initialize main GUI figure
            hFig = figure('tag','DLHFig','name',str1,'numbertitle','off','position',position,...
                'CloseRequestFcn', 'DLHGui(''closeRequest'')','menubar','none','resize','off','color',defaultColor);
        else
            figure(findobj('tag','DLHFig'))
            return
        end
        %stateS.hFig = hFig;

        figureWidth = position(3); figureHeight = position(4);
        posTop = figureHeight-topMarginHeight;

        % create title handles
        handle(1) = uicontrol(hFig,'tag','titleFrame','units','pixels','Position',[150 figureHeight-topMarginHeight+5 500 40 ],'Style','frame','backgroundColor',defaultColor);
        handle(2) = uicontrol(hFig,'tag','title','units','pixels','Position',[151 figureHeight-topMarginHeight+10 498 30 ], 'String','Dose Location Histogram','Style','text', 'fontSize',10,'FontWeight','Bold','HorizontalAlignment','center','backgroundColor',defaultColor);
        handle(3) = uicontrol(hFig,'tag','titleFrame','units','pixels','Position',[leftMarginWidth-20 250 1 figureHeight-topMarginHeight-260 ],'Style','frame','backgroundColor',defaultColor);

        % create Dose and structure handles
        inputH(1) = uicontrol(hFig,'tag','doseStructTitle','units','pixels','Position',[20 posTop-40 150 20], 'String','DOSE & STRUCTURE','Style','text', 'fontSize',9.5,'FontWeight','Bold','BackgroundColor',defaultColor,'HorizontalAlignment','left');
        prefix = 'Select a dose.';
        doseList = {prefix, planC{indexS.dose}.fractionGroupID};
        prefix = 'Select a structure.';
        structList = {prefix, planC{indexS.structures}.structureName};
        inputH(2) = uicontrol(hFig,'tag','doseStatic','units','pixels','Position',[20 posTop-70 120 20], 'String','Select Dose','Style','text', 'fontSize',8,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','right');
        inputH(3) = uicontrol(hFig,'tag','doseSelect','units','pixels','Position',[150 posTop-70 120 20], 'String',doseList,'Style','popup', 'fontSize',9,'FontWeight','normal','BackgroundColor',[1 1 1],'HorizontalAlignment','left');
        inputH(4) = uicontrol(hFig,'tag','structStatic','units','pixels','Position',[20 posTop-100 120 20], 'String','Select Structure','Style','text', 'fontSize',8,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','right');
        inputH(5) = uicontrol(hFig,'tag','structSelect','units','pixels','Position',[150 posTop-100 120 20], 'String',structList,'Style','popup', 'fontSize',9,'FontWeight','normal','BackgroundColor',[1 1 1],'HorizontalAlignment','left');

        inputH(6) = uicontrol(hFig,'tag','criteriaTitle','units','pixels','Position',[20 posTop-140 180 20], 'String','CRITERIA','Style','text', 'fontSize',9.5,'FontWeight','Bold','BackgroundColor',defaultColor,'HorizontalAlignment','left');
        inputH(7) = uicontrol(hFig,'tag','doseStatic','units','pixels','Position',[20 posTop-170 40 20], 'String','Dose','Style','text', 'fontSize',8,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','center');
        inputH(8) = uicontrol(hFig,'tag','doseOperator','units','pixels','Position',[70 posTop-170 40 20], 'String',{'<=','>='},'Style','popup', 'fontSize',8,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','center');
        inputH(9) = uicontrol(hFig,'tag','doseEdit','units','pixels','Position',[120 posTop-170 40 20], 'String','','Style','edit', 'fontSize',8,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','center');
        inputH(10) = uicontrol(hFig,'tag','doseUnitsStatic','units','pixels','Position',[160 posTop-170 80 20], 'String','Gy or cGy','Style','text', 'fontSize',8,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','center');

        %Create Dose-Stats handles
        uicontrol(hFig,'tag','titleFrame','units','pixels','Position',[20 figureHeight-topMarginHeight-525 760 200 ],'Style','frame','backgroundColor',defaultColor);

        %Define DLH-plot fraction Axis
        plotH(1) = axes('parent',hFig,'tag','dvhAxisFract','tickdir', 'out','nextplot', 'add','units','pixels','Position',[leftMarginWidth+40 posTop*2/4-00 figureWidth-leftMarginWidth-100 posTop*0.9/2], 'color',[1 1 1],'YAxisLocation','right','fontSize',8,'visible','off');

        %Define DLH-plot Axis
        plotH(2) = axes('parent',hFig,'tag','dvhAxis','tickdir', 'out','nextplot', 'add','units','pixels','Position',[leftMarginWidth+40 posTop*2/4-00 figureWidth-leftMarginWidth-100 posTop*0.9/2], 'color',[1 1 1],'YAxisLocation','left','fontSize',8,'visible','off');

        yLim = get(plotH(1), 'yLim');
        %dvhStatH(end+1) = line([0 0], [yLim(1) yLim(2)], 'tag','motionLine','color', 'blue', 'parent', plotH(1), 'hittest', 'off','visible','off');

        submitH(1) = uicontrol(hFig,'tag','SubmitPush','units','pixels','Position',[20 posTop-225 160 25], 'String','Compute DLH','Style','push', 'fontSize',9,'FontWeight','normal','BackgroundColor',[1 1 1],'HorizontalAlignment','center','callback','DLHGui(''COMPUTE_DVH'')');
        submitH(2) = uicontrol(hFig,'tag','ClearPush','units','pixels','Position',[20 posTop-275 160 25], 'String','Clear all DLHs','Style','push', 'fontSize',9,'FontWeight','normal','BackgroundColor',[1 1 1],'HorizontalAlignment','center','callback','DLHGui(''CLEAR_ALL_DLHS'')');

        %Create handles for computed DLH

        %control to display legend in new figure
        dvhStatH(1) = uicontrol(hFig, 'tag','showLegend', 'style', 'checkbox', 'position', [700 posTop-20 100 20], 'string', 'Legend', 'horizontalAlignment', 'center','BackgroundColor',defaultColor, 'callback', 'DLHGui(''LEGENDCALL'')','visible','off');

        %Column headings
        dvhStatH(end+1) = uicontrol(hFig,'tag','sNo','units','pixels','Position',[100 posTop-350 35 20], 'String','DLH #','Style','text', 'fontSize',9,'FontWeight','bold','BackgroundColor',defaultColor,'HorizontalAlignment','center');
        dvhStatH(end+1) = uicontrol(hFig,'tag','doseSelected','units','pixels','Position',[135 posTop-350 120 20], 'String','Dose','Style','text', 'fontSize',9,'FontWeight','bold','BackgroundColor',defaultColor,'HorizontalAlignment','center');
        dvhStatH(end+1) = uicontrol(hFig,'tag','structSelected','units','pixels','Position',[270 posTop-350 120 20], 'String','Structure','Style','text', 'fontSize',9,'FontWeight','bold','BackgroundColor',defaultColor,'HorizontalAlignment','center');
        dvhStatH(end+1) = uicontrol(hFig,'tag','criteriaSelected','units','pixels','Position',[400 posTop-350 150 20], 'String','Criteria','Style','text', 'fontSize',9,'FontWeight','bold','BackgroundColor',defaultColor,'HorizontalAlignment','center');
        dvhStatH(end+1) = uicontrol(hFig,'tag','volumeSelected','units','pixels','Position',[560 posTop-350 100 20], 'String','Vol (cc)','Style','text', 'fontSize',9,'FontWeight','bold','BackgroundColor',defaultColor,'HorizontalAlignment','center');

        numDLH = 5;
        for i=1:numDLH
            rowIndex = rem(i,5);
            if rowIndex == 0
                rowIndex = 5;
            end
            dvhStatH(end+1) = uicontrol(hFig,'tag',['DeleteDLH',num2str(i)],'units','pixels','Position',[45 posTop-350-30*rowIndex 25 20], 'String','--','Style','push', 'fontSize',9,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment', 'center', 'TooltipString','Delete this DLH', 'callBack',['DLHGui(''DELETE_DVH'',',num2str(i),')']);
            dvhStatH(end+1) = uicontrol(hFig,'tag',['sNo',num2str(i)],'units','pixels','Position',[100 posTop-350-30*rowIndex 30 20], 'String',num2str(i),'Style','text', 'fontSize',9,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','center');
            dvhStatH(end+1) = uicontrol(hFig,'tag',['doseSelected',num2str(i)],'units','pixels','Position',[135 posTop-350-30*rowIndex 120 20], 'String','Dose','Style','text', 'fontSize',9,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','center');
            dvhStatH(end+1) = uicontrol(hFig,'tag',['structSelected',num2str(i)],'units','pixels','Position',[270 posTop-350-30*rowIndex 120 20], 'String','Structure','Style','text', 'fontSize',9,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','center');
            dvhStatH(end+1) = uicontrol(hFig,'tag',['criteriaSelected',num2str(i)],'units','pixels','Position',[400 posTop-350-30*rowIndex 150 20], 'String','Criteria','Style','text', 'fontSize',9,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','center');
            dvhStatH(end+1) = uicontrol(hFig,'tag',['volumeSelected',num2str(i)],'units','pixels','Position',[560 posTop-350-30*rowIndex 100 20], 'String','% Total Vol','Style','text', 'fontSize',9,'FontWeight','normal','BackgroundColor',defaultColor,'HorizontalAlignment','center');
        end

        dvhStatH(end+1) = uicontrol(hFig,'tag','DLHslider','units','pixels','Position',[760 figureHeight-topMarginHeight-525 20 200], 'Style','slider', 'BackgroundColor',defaultColor,'HorizontalAlignment','center', 'max', 5, 'min', 1, 'value', 1, 'sliderStep',[1 1], 'callback','DLHGui(''SLIDER_CLICKED'')');

        DVHInitS = struct('distV','',...
            'volsV','',...
            'binWidth','',...
            'doseNum','',...
            'structNum','',...
            'doseCutoff','',...
            'doseOperatorVal','',...
            'lineStyle','',...
            'hPlot','');

        DVHInitS(1) = [];

        ud.DVH = DVHInitS;
        ud.handle.inputH = inputH;
        ud.handle.DVHStatH = dvhStatH;
        set(ud.handle.DVHStatH,'visible','off')

        ud.currentDVHS.hLB = [];
        ud.currentDVHS.hUB = [];
        ud.currentDVHS.hObserved = [];
        ud.currentDVHS.hPatch = [];

        set(hFig,'userdata',ud);

    case 'COMPUTE_DVH'

        hFig = findobj('tag','DLHFig');
        ud = get(hFig,'userdata');

        doseNum         = get(findobj(ud.handle.inputH,'tag','doseSelect'),'value')-1;
        structNum       = get(findobj(ud.handle.inputH,'tag','structSelect'),'value')-1;
        doseCutoff      = str2num(get(findobj(ud.handle.inputH,'tag','doseEdit'),'string'));
        doseOperatorVal = get(findobj(ud.handle.inputH,'tag','doseOperator'),'value');

        if doseNum == 0
            warndlg('Please select dose.','Invalid dose','modal')
            return;
        end

        if structNum == 0
            warndlg('Please select structure.','Invalid structure','modal')
            return;
        end

        if isempty(doseCutoff)
            warndlg('Incorrect Dose cutoff. Please specify a valid number.','Invalid dose cutoff','modal')
            return;
        end

        [distV, volsV] = getDLH(doseNum, structNum, doseCutoff, doseOperatorVal, planC);

        %Compute total volume of the structure
        totalVol = sum(volsV);

        hFig = findobj('tag','DLHFig');
        ud = get(hFig,'userdata');

        numDVH = length(ud.DVH);
        ud.DVH(numDVH+1).doseNum         = doseNum;
        ud.DVH(numDVH+1).structNum       = structNum;
        ud.DVH(numDVH+1).doseCutoff      = doseCutoff;
        ud.DVH(numDVH+1).doseOperatorVal = doseOperatorVal;
        ud.DVH(numDVH+1).totalVolume     = totalVol;
        ud.DVH(numDVH+1).lineStyle       = '';
        ud.DVH(numDVH+1).hPlot           = [];
        ud.DVH(numDVH+1).distV           = distV;
        ud.DVH(numDVH+1).volsV           = volsV;

        set(hFig,'userdata',ud)

        DLHGui('PLOT_DVH',numDVH+1)

        DLHGui('REFRESH')

    case 'PLOT_DVH'

        hFig = findobj('tag','DLHFig');

        %Get Robust DVH structure to plot
        ud = get(hFig,'userdata');
        hAxis = findobj('tag','dvhAxis');
        hAxisFract = findobj('tag','dvhAxisFract');

        DVHnum = varargin{1};
        hLegend = findobj(ud.handle.DVHStatH,'tag','showLegend');
        if DVHnum == 0
            try
                delete(ud.currentDVHS.hHighlight)
                set(ud.handle.DVHStatH(3:end),'visible','off')
            end
            ud.currentDVHS.hLB = [];
            ud.currentDVHS.hUB = [];
            ud.currentDVHS.hObserved = [];
            ud.currentDVHS.hPatch = [];
            ud.currentDVHS.hMean = [];
            ud.currentDVHS.hHighlight = [];
            for i=1:length(ud.DVH)
                set(ud.DVH(i).handles,'visible','off')
            end
            set(hAxis,'visible','off')
            set(hAxisFract,'visible','off')
            set(hAxis, 'buttonDownFcn', '');
            set(hLegend,'visible','off')
            set(hFig,'userdata',ud);
            return;
        end
        set(hLegend,'visible','on')

        DVHs = ud.DVH(DVHnum);

        hSlider = findobj(ud.handle.DVHStatH,'tag','DLHslider');

        set(hSlider,'value',ceil(length(ud.DVH)/5)-ceil(DVHnum/5)+1,'min',1,'max',ceil(length(ud.DVH)/5),'sliderStep',[1/max(1,(ceil(length(ud.DVH)/5)-1)) 1/max(1,(ceil(length(ud.DVH)/5)-1))])

        %Structure color
        colorV = planC{indexS.structures}(DVHs.structNum).structureColor;

        %Delete old handles
        try, delete(DVHs.handles), end
        DVHs.handles = [];

        distV = DVHs.distV;
        volsV = DVHs.volsV;
        binWidth = 0.05; %DVHs.binWidth;

        %Plot Current DVH
        [distBinsV, volsHistV] = doseHist(distV, volsV, binWidth);
        cumVolsV  = cumsum(volsHistV);
        cumVols2V = cumVolsV(end) - cumVolsV;  %cumVolsV is the cumulative volume of the corresponding dose
        %Get lineStyle
        lineStyleC = repmat({'-','--','-.',':'},1,100);
        indPrevious = find(([ud.DVH.doseNum] == ud.DVH(DVHnum).doseNum) | ([ud.DVH.structNum] == ud.DVH(DVHnum).structNum));
        lineStyle = lineStyleC{length(indPrevious)};
        ud.DVH(DVHnum).lineStyle = lineStyle;
        %Cumulative
        %hDLH     = plot(hAxis,distBinsV, cumVols2V,'LineWidth',2,'color',colorV,'parent',hAxis,'lineStyle',lineStyle);
        %Cumulative
        hDLH     = plot(hAxis,distBinsV, cumVolsV,'LineWidth',2,'color',colorV,'parent',hAxis,'lineStyle',lineStyle);
        %Differential
        %hDLH     = plot(hAxis,distBinsV, volsHistV,'LineWidth',2,'color',colorV,'parent',hAxis,'lineStyle',lineStyle);
        ud.DVH(DVHnum).hPlot = hDLH;

        totalVol = ud.DVH(DVHnum).totalVolume;
        xDLHVals = [distBinsV];
        yDLHVals = [cumVolsV];
        [xDLHVals, aInd] = unique(xDLHVals);
        yDLHVals = yDLHVals(aInd);
        ud.currentDVHS.xDLHVals = xDLHVals;
        ud.currentDVHS.yDLHVals = yDLHVals;
        ud.currentDVHS.hDLH = hDLH;
        DVHs.handles = [DVHs.handles hDLH];

        xlabel(hAxis,'Distance from Surface (cm)')
        ylabel(hAxis,'volume (cc)')
        set(hAxis,'visible','on')
        grid(hAxis,'on')
        axis(hAxis,'tight')

        %Compute Fractions of total volume
        set(hAxisFract,'visible','on')
        yLabelsV = get(hAxis,'yTick');
        fractVolsV = linspace(min(cumVolsV(1),yLabelsV(1)),max(cumVolsV(end),yLabelsV(end)),10);
        fractVolsV = fractVolsV/totalVol*100;
        set(hAxisFract,'yLim',[fractVolsV(1) fractVolsV(end)])
        set(hAxisFract,'xTick',[]);
        ylabel(hAxisFract,'fraction of total volume')

        set(hFig,'userdata',ud)

    case 'REFRESH'

        hFig = findobj('tag','DLHFig');

        %Get Robust DVH structure to plot
        ud = get(hFig,'userdata');

        numDVH = length(ud.DVH);

        %Turn Off all DVH stats
        set(ud.handle.DVHStatH,'visible','off')

        if numDVH > 0
            hSno = findobj(ud.handle.DVHStatH,'tag','sNo');
            hDose = findobj(ud.handle.DVHStatH,'tag','doseSelected');
            hStruct = findobj(ud.handle.DVHStatH,'tag','structSelected');
            hCriteria = findobj(ud.handle.DVHStatH,'tag','criteriaSelected');
            hVolume = findobj(ud.handle.DVHStatH,'tag','volumeSelected');
            set([hSno hDose hStruct hCriteria hVolume], 'visible','on')
            hLegend = findobj(ud.handle.DVHStatH,'tag','showLegend');
            set(hLegend,'visible','on')
        end

        hSlider = findobj(ud.handle.DVHStatH,'tag','DLHslider');
        if numDVH > 5
            set(hSlider,'visible','on')
        else
            set(hSlider,'visible','off')
        end

        sliderVal = get(hSlider,'value');
        sliderMax = get(hSlider,'max');
        sliderVal = sliderMax - sliderVal + 1;
        count = 1;
        for i=(sliderVal-1)*5+1:min(sliderVal*5,numDVH)
            hDelete = findobj(ud.handle.DVHStatH,'tag',['DeleteDLH',num2str(count)]);
            hSno = findobj(ud.handle.DVHStatH,'tag',['sNo',num2str(count)]);
            hDose = findobj(ud.handle.DVHStatH,'tag',['doseSelected',num2str(count)]);
            hStruct = findobj(ud.handle.DVHStatH,'tag',['structSelected',num2str(count)]);
            hCriteria = findobj(ud.handle.DVHStatH,'tag',['criteriaSelected',num2str(count)]);
            hVolume = findobj(ud.handle.DVHStatH,'tag',['volumeSelected',num2str(count)]);
            set([hDelete hSno hDose hStruct hCriteria hVolume], 'visible','on')
            set(hDelete, 'callback', ['DLHGui(''CLEAR_DLH'',',num2str(i),')'])
            set(hSno,'string',num2str(i))
            set(hDose,'string',planC{indexS.dose}(ud.DVH(i).doseNum).fractionGroupID)
            set(hStruct,'string',planC{indexS.structures}(ud.DVH(i).structNum).structureName)
            set(hVolume,'string',num2str(ud.DVH(i).totalVolume))
            criteriaStr = 'Dose ';
            if ud.DVH(i).doseOperatorVal == 1
                criteriaStr = [criteriaStr, '<=', num2str(ud.DVH(i).doseCutoff)];
            else
                criteriaStr = [criteriaStr, '>=', num2str(ud.DVH(i).doseCutoff)];
            end
            set(hCriteria,'string',criteriaStr)
            count = count + 1;
        end

    case 'SLIDER_CLICKED'

        sliderVal = get(gcbo,'value');

        sliderVal = round(sliderVal);

        set(gcbo,'value',sliderVal)

        DLHGui('REFRESH')

    case 'CLEAR_DLH'

        dlhNum = varargin{1};

        hFig = findobj('tag','DLHFig');

        %Get Robust DVH structure to plot
        ud = get(hFig,'userdata');

        delete(ud.DVH(dlhNum).hPlot)

        ud.DVH(dlhNum) = [];

        DVHnum = min(length(ud.DVH), dlhNum);

        hSlider = findobj(ud.handle.DVHStatH,'tag','DLHslider');

        set(hSlider,'value',min(ceil(length(ud.DVH)/5),ceil(length(ud.DVH)/5)-ceil(DVHnum/5)+1),'min',min(1,ceil(length(ud.DVH)/5)),'max',ceil(length(ud.DVH)/5),'sliderStep',[1/max(1,(ceil(length(ud.DVH)/5)-1)) 1/max(1,(ceil(length(ud.DVH)/5)-1))])

        set(hFig,'userdata', ud)

        if length(ud.DVH) == 0
           DLHGui('CLEAR_ALL_DLHS')
        else
            DLHGui('REFRESH')
        end

    case 'CLEAR_ALL_DLHS'

        ButtonName = questdlg('This will delete all DLHs. Do you wish to continue?', 'Delete all DLHs?', 'Yes','No','Yes');

        if strcmpi(ButtonName,'no')
            return
        end

        hFig = findobj('tag','DLHFig');

        %Get Robust DVH structure to plot
        ud = get(hFig,'userdata');

        ud.DVH(:) = [];

        hAxis = findobj('tag','dvhAxis');
        hAxisFract = findobj('tag','dvhAxisFract');
        set(hAxis,'nextPlot','add')
        set(hAxisFract,'nextPlot','add')
        %delete([ud.DVH.handles])
        cla(hAxis)
        set(hAxis,'visible','off')
        set(hAxisFract,'visible','off')
        %DVHs.handles = [];

        set(hFig,'userdata', ud)

        DLHGui('REFRESH')

    case 'TOGGLE_VISIBILITY'
        hFig = findobj('tag','DLHFig');

        %Get Robust DVH structure to plot
        ud = get(hFig,'userdata');

        hDVH_num = findobj(ud.handle.DVHStatH,'tag','dvhSelect');
        DVHnum = get(hDVH_num,'value') - 1;

        value = get(gcbo,'value');
        DVHs = ud.DVH(DVHnum);
        if value
            set(DVHs.handles,'visible','on')
        else
            set(DVHs.handles,'visible','off')
            set(ud.currentDVHS.hHighlight,'visible','off')
        end

    case 'PLOTMOTION'
    %A DVH is selected, the plot has been clicked and motion is occuring. Update guide line/fields.
        hFig = findobj('tag','DLHFig');
        hAxis = findobj('tag','dvhAxis');
        ud = get(hFig, 'userdata');
        cP = get(hAxis, 'currentpoint');
        dose = cP(1,1);
        xLim = get(hAxis, 'xLim');
        if dose < xLim(1)
            dose = xLim(1);
        elseif dose > xLim(2)
            dose = xLim(2);
        end
        observVol = interp1(ud.currentDVHS.xObsVals, ud.currentDVHS.yObsVals, dose);
        if isnan(observVol)
            observVol = 0;
        end
        meanVol = interp1(ud.currentDVHS.xMeanVals, ud.currentDVHS.yMeanVals, dose);
        if isnan(meanVol)
            meanVol = 0;
        end
        lbVol = interp1(ud.currentDVHS.xLBVals, ud.currentDVHS.yLBVals, dose);
        if isnan(lbVol)
            lbVol = 0;
        end
        ubVol = interp1(ud.currentDVHS.xUBVals, ud.currentDVHS.yUBVals, dose);
        if isnan(ubVol)
            ubVol = 0;
        end

        hvolObser = findobj(ud.handle.DVHStatH,'tag','volAbovDoseObserTxt');
        set(hvolObser,'string',num2str(observVol))
        hvolLBD  = findobj(ud.handle.DVHStatH,'tag','volAbovDoseLBTxt');
        set(hvolLBD,'string',num2str(lbVol))
        hvolUBD  = findobj(ud.handle.DVHStatH,'tag','volAbovDoseUBTxt');
        set(hvolUBD,'string',num2str(ubVol))
        hDose = findobj(ud.handle.DVHStatH,'tag','volAbovDoseEdit');
        set(hDose,'string',num2str(dose))
        hvolMeanD  = findobj(ud.handle.DVHStatH,'tag','volAbovDoseMeanTxt');
        set(hvolMeanD,'string',num2str(meanVol))
        hMotionLine = findobj(ud.handle.DVHStatH,'tag','motionLine');
        set(hMotionLine, 'xdata', [dose dose], 'visible', 'on');

    case 'CLICKINPLOT'
    %A DVH is selected and the plot has been clicked, update guide line/fields.
        hFig = findobj('tag','DLHFig');
        hAxis = findobj('tag','dvhAxis');
        ud = get(hFig, 'userdata');
        set(hFig, 'WindowButtonUpFcn', 'DLHGui(''UNCLICKINPLOT'')');
        set(hFig, 'WindowButtonMotionFcn', 'DLHGui(''PLOTMOTION'')');
        cP = get(hAxis, 'currentpoint');
        %Interpolate Vol, display.
        dose = cP(1,1);
        xLim = get(hAxis, 'xLim');
        if dose < xLim(1)
            dose = xLim(1);
        elseif dose > xLim(2)
            dose = xLim(2);
        end
        observVol = interp1(ud.currentDVHS.xObsVals, ud.currentDVHS.yObsVals, dose);
        if isnan(observVol)
            observVol = 0;
        end
        meanVol = interp1(ud.currentDVHS.xMeanVals, ud.currentDVHS.yMeanVals, dose);
        if isnan(meanVol)
            meanVol = 0;
        end
        lbVol = interp1(ud.currentDVHS.xLBVals, ud.currentDVHS.yLBVals, dose);
        if isnan(lbVol)
            lbVol = 0;
        end
        ubVol = interp1(ud.currentDVHS.xUBVals, ud.currentDVHS.yUBVals, dose);
        if isnan(ubVol)
            ubVol = 0;
        end

        hvolObser = findobj(ud.handle.DVHStatH,'tag','volAbovDoseObserTxt');
        set(hvolObser,'string',num2str(observVol))
        hvolLBD  = findobj(ud.handle.DVHStatH,'tag','volAbovDoseLBTxt');
        set(hvolLBD,'string',num2str(lbVol))
        hvolUBD  = findobj(ud.handle.DVHStatH,'tag','volAbovDoseUBTxt');
        set(hvolUBD,'string',num2str(ubVol))
        hDose = findobj(ud.handle.DVHStatH,'tag','volAbovDoseEdit');
        set(hDose,'string',num2str(dose))
        hvolMeanD  = findobj(ud.handle.DVHStatH,'tag','volAbovDoseMeanTxt');
        set(hvolMeanD,'string',num2str(meanVol))
        hMotionLine = findobj(ud.handle.DVHStatH,'tag','motionLine');
        set(hMotionLine, 'xdata', [dose dose], 'visible', 'on');

    case 'UNCLICKINPLOT'
        hFig = findobj('tag','DLHFig');
        ud = get(hFig, 'userdata');
        set(hFig, 'WindowButtonMotionFcn', '');
        set(hFig, 'WindowButtonUpFcn', '');
        set(hFig, 'userdata', ud);

    case 'DOSEVAL'
    %A value has been entered into the doseVal field.
        hFig = get(gcbo, 'parent');
        dose = str2double(get(gcbo, 'string'));
        ud = get(hFig, 'userdata');
        hAxis = findobj('tag','dvhAxis');
        xLim = get(hAxis, 'xLim');
        if dose < xLim(1)
            dose = xLim(1);
        elseif dose > xLim(2)
            dose = xLim(2);
        end

        obserVol = interp1(ud.currentDVHS.xObsVals, ud.currentDVHS.yObsVals, dose);
        if isnan(obserVol)
            obserVol = 0;
        end
        LBVol = interp1(ud.currentDVHS.xLBVals, ud.currentDVHS.yLBVals, dose);
        if isnan(LBVol)
            LBVol = 0;
        end
        UBVol = interp1(ud.currentDVHS.xUBVals, ud.currentDVHS.yUBVals, dose);
        if isnan(UBVol)
            UBVol = 0;
        end

        hvolObser = findobj(ud.handle.DVHStatH,'tag','volAbovDoseObserTxt');
        set(hvolObser,'string',num2str(obserVol))
        hvolLBD  = findobj(ud.handle.DVHStatH,'tag','volAbovDoseLBTxt');
        set(hvolLBD,'string',num2str(LBVol))
        hvolUBD  = findobj(ud.handle.DVHStatH,'tag','volAbovDoseUBTxt');
        set(hvolUBD,'string',num2str(UBVol))
        hMotionLine = findobj(ud.handle.DVHStatH,'tag','motionLine');
        set(hMotionLine, 'xdata', [dose dose], 'visible', 'on');

    case 'LEGENDCALL'

        %executes when "Show Legend" is toggled on or off
        value = get(gcbo,'value');
        hLegend = findobj('tag','DVH_Legend');
        close(hLegend)
        if value == 0
            return;
        end
        hFig = get(gcbo, 'parent');
        ud = get(hFig, 'userdata');
        hLegend = figure('tag','DVH_Legend','name','DVH Legend','numberTitle','off','menubar','none','color','w');
        position = get(hLegend,'position');
        figColor = get(hLegend,'color');
        axisLegend = axes('units', 'normalized', 'Position', [0 0 1 1], 'color', figColor, 'ytick',[],'xtick',[], 'box', 'off', 'parent', hLegend,'nextPlot','add','units','normalized','visible','off');
        numLines = length(ud.DVH);
        dy = 0.8/numLines;
        position(3) = 400;
        position(4) = (numLines+1)*30;
        set(hLegend,'position',position)

        for i = 1:numLines
            Color = planC{indexS.structures}(ud.DVH(i).structNum).structureColor;
            line([0.05 0.15],[0.8-(i-1)*dy 0.8-(i-1)*dy],'LineStyle',ud.DVH(i).lineStyle,'LineWidth',2,'Color',Color,'parent',axisLegend)
            if ud.DVH(i).doseOperatorVal == 1
                criteriaStr = ['dose ', '<= ', num2str(ud.DVH(i).doseCutoff)];
            else
                criteriaStr = ['dose ', '>= ', num2str(ud.DVH(i).doseCutoff)];
            end
            txt = [planC{indexS.structures}(ud.DVH(i).structNum).structureName,'(',planC{indexS.dose}(ud.DVH(i).doseNum).fractionGroupID,'), ',criteriaStr];
            text(0.18,0.8-(i-1)*dy,txt)
        end
        axis(axisLegend,[0 1 0 1])

    case 'CLOSEREQUEST'

        closereq

end


%> @file classDocumentationExample.m
%> @brief File used to show an example of class description
% ======================================================================
%> @brief Here we have a brief description of the class.
%
%> And here we can put some more detailed informations about the class.
% ======================================================================
classdef  (InferiorClasses = {?class1,?class2}) classDocumentationExample

  properties (Access = protected)
    %> Description of a protected property
    protectedProperty
  end
  properties (Access = public)
    %> Description of a public property
    publicProperty
  end
  properties (Access = private)
    %> Description of a private property
    pivateProperty
  end
  properties (Constant = true)
    %> Description of a constant property
    constantProperty = {'1', '2', 'trois'};
  end
  properties
    %> Description of the first property of the class
    first_property = []
    %> Description of the second property of the class
    second_property = []
    %> Description of the third property of the class
    third_property = [1  2];
  end
  events
    %> Description of first event
    FirstEvent
    %> Description of second event
    SecondEvent
  end
  %> Description of the enumeration.
  enumeration
    %> Description of the first item
    one (1)
    %> Description of the second item
    two (2)
    %> Description of the third item
    three
  end
  methods
    % ======================================================================
    %> @brief Class constructor
    %>
    %> More detailed description of what the constructor does.
    %>
    %> @param param1 Description of first parameter
    %> @param anotherParam Description of the second parametere
    %>
    %> @return instance of the classDocumentationExample class.
    % ======================================================================
    function obj = classDocumentationExample(param1, anotherParam)
    end

    % ======================================================================
    %> @brief Brief description of the exampleMethod1 method
    %>
    %> @param obj instance of the classDocumentationExample class.
    % ======================================================================
    function exampleMethod1(obj)
    end

    % ======================================================================
    %> @brief Brief description of the exampleMethod2 method
    %>
    %> @param obj instance of the classDocumentationExample class.
    %> @retval ret return value of this method
    % ======================================================================
    function ret = exampleMethod2(obj)
    end
  end
  methods (Static=true)
    % ======================================================================
    %> @brief Brief description of the exampleStaticMethod method
    %>
    %> This method is static and public, with an inused (~) argument
    %> @param param1 Description of the parameter
    %> @param param2 Description of the parameter
    %> @retval out return value of this method
    % ======================================================================
    function out = exampleStaticMethod(param1, ~, param2)
    end
  end
  methods (Static, Access=private)
    % ======================================================================
    %> @brief Brief description of the exampleStaticPrivateMethod method
    %>
    %> This method is static and private
    %> @param param1 Description of the parameter
    %> @param param2 Description of the parameter
    %> @retval out return value of this method
    % ======================================================================
    function out = exampleStaticPrivateMethod(param1, param2)
    end
  end
  methods (Access=protected, Static)
    % ======================================================================
    %> @brief Brief description of the exampleStaticProtectedMethod method
    %>
    %> This method is static and protected
    %> @param param1 Description of the parameter
    %> @retval out return value of this method
    % ======================================================================
    function out = exampleStaticProtectedMethod(param1)
    end
  end
  methods (Access=public, Static = true)
    % ======================================================================
    %> @brief Brief description of the exampleStaticPublicMethod method
    %>
    %> This method is static and public
    %> @param param1 Description of the parameter
    %> @retval out return value of this method
    % ======================================================================
    function out = exampleStaticPublicMethod(param1)
    end
  end
  methods (Access=private)
    % ======================================================================
    %> @brief Brief description of the examplePrivateMethod method
    %>
    %> This method is private
    %> @param param1 Description of the parameter
    %> @retval out return value of this method
    % ======================================================================
    function out = examplePrivateMethod(param1)
    end
  end
  methods (Access=protected)
    % ======================================================================
    %> @brief Brief description of the exampleProtectedMethod method
    %>
    %> This method is protected
    %> @param param1 Description of the parameter
    %> @retval out return value of this method
    % ======================================================================
    function out = exampleProtectedMethod(param1)
    end
  end
  methods (Access=public, Static=false)
    % ======================================================================
    %> @brief Brief description of the examplePublicMethod2 method
    %>
    %> This method is public and not static
    %> @param param1 Description of the parameter
    %> @retval out return value of this method
    % ======================================================================
    function out = examplePublicMethod2(param1)
    end
  end
  methods (Access=public, ~Static)
    % ======================================================================
    %> @brief Brief description of the exampleNonStaticPublicMethod3 method
    %>
    %> This method is public and not static
    %> @param param1 Description of the parameter
    %> @retval out return value of this method
    % ======================================================================
    function out = exampleNonStaticPublicMethod3(param1)
    end
  end
  methods (Abstract = true)
    % ======================================================================
    %> @brief Brief description of the exampleAbstractMethod method
    %>
    %> This method is abstract : only the signature of this function is
    %> declared.
    %> @param param1 Description of the first parameter
    %> @param param2 Description of the second parameter
    %> @retval out return value of this method
    % ======================================================================
    out = exampleAbstractMethod(param1, param2);
end


%  Save NIFTI dataset. Support both *.nii and *.hdr/*.img file extension.
%  If file extension is not provided, *.hdr/*.img will be used as default.
%
%  Usage: save_nii(nii, filename, [old_RGB])
%
%  nii.hdr - struct with NIFTI header fields (from load_nii.m or make_nii.m)
%
%  nii.img - 3D (or 4D) matrix of NIFTI data.
%
%  filename - NIFTI file name.
%
%  old_RGB    - an optional boolean variable to handle special RGB data
%       sequence [R1 R2 ... G1 G2 ... B1 B2 ...] that is used only by
%       AnalyzeDirect (Analyze Software). Since both NIfTI and Analyze
%       file format use RGB triple [R1 G1 B1 R2 G2 B2 ...] sequentially
%       for each voxel, this variable is set to FALSE by default. If you
%       would like the saved image only to be opened by AnalyzeDirect
%       Software, set old_RGB to TRUE (or 1). It will be set to 0, if it
%       is default or empty.
%
%  Tip: to change the data type, set nii.hdr.dime.datatype,
%	and nii.hdr.dime.bitpix to:
%
%     0 None                     (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN
%     1 Binary                         (ubit1, bitpix=1) % DT_BINARY
%     2 Unsigned char         (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8
%     4 Signed short                  (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16
%     8 Signed integer                (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32
%    16 Floating point    (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32
%    32 Complex, 2 float32      (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64
%    64 Double precision  (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64
%   128 uint RGB                  (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24
%   256 Signed char            (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8
%   511 Single RGB              (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96
%   512 Unsigned short               (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16
%   768 Unsigned integer             (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32
%  1024 Signed long long              (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64
%  1280 Unsigned long long           (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64
%  1536 Long double, float128  (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128
%  1792 Complex128, 2 float64  (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128
%  2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128
%
%  Part of this file is copied and modified from:
%  http://www.mathworks.com/matlabcentral/fileexchange/1878-mri-analyze-tools
%
%  NIFTI data format can be found on: http://nifti.nimh.nih.gov
%
%  - Jimmy Shen (jimmy@rotman-baycrest.on.ca)
%  - "old_RGB" related codes in "save_nii.m" are added by Mike Harms (2006.06.28)
%
function save_nii(nii, fileprefix, old_RGB)

   if ~exist('nii','var') || isempty(nii) || ~isfield(nii,'hdr') || ...
	~isfield(nii,'img') || ~exist('fileprefix','var') || isempty(fileprefix)

      error('Usage: save_nii(nii, filename, [old_RGB])');
   end

   if isfield(nii,'untouch') && nii.untouch == 1
      error('Usage: please use ''save_untouch_nii.m'' for the untouched structure.');
   end

   if ~exist('old_RGB','var') || isempty(old_RGB)
      old_RGB = 0;
   end

   v = version;

   %  Check file extension. If .gz, unpack it into temp folder
   %
   if length(fileprefix) > 2 && strcmp(fileprefix(end-2:end), '.gz')

      if ~strcmp(fileprefix(end-6:end), '.img.gz') && ...
	 ~strcmp(fileprefix(end-6:end), '.hdr.gz') && ...
	 ~strcmp(fileprefix(end-6:end), '.nii.gz')

         error('Please check filename.');
      end

      if str2num(v(1:3)) < 7.1 || ~usejava('jvm')
         error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.');
      else
         gzFile = 1;
         fileprefix = fileprefix(1:end-3);
      end
   end

   filetype = 1;

   %  Note: fileprefix is actually the filename you want to save
   %
   if ~isempty(findstr('.nii',fileprefix)) && ...
                        strcmp(fileprefix(end-3:end), '.nii')
      filetype = 2;
      fileprefix(end-3:end)='';
   end

   if ~isempty(findstr('.hdr',fileprefix)) && ...
                        strcmp(fileprefix(end-3:end), '.hdr')
      fileprefix(end-3:end)='';
   end

   if ~isempty(findstr('.img',fileprefix)) && ...
                        strcmp(fileprefix(end-3:end), '.img')
      fileprefix(end-3:end)='';
   end

   write_nii(nii, filetype, fileprefix, old_RGB);

   %  gzip output file if requested
   %
   if exist('gzFile', 'var')
      if filetype == 1
         gzip([fileprefix, '.img']);
         delete([fileprefix, '.img']);
         gzip([fileprefix, '.hdr']);
         delete([fileprefix, '.hdr']);
      elseif filetype == 2
         gzip([fileprefix, '.nii']);
         delete([fileprefix, '.nii']);
      end;
   end;

   if filetype == 1

      %  So earlier versions of SPM can also open it with correct originator
      %
      M=[[diag(nii.hdr.dime.pixdim(2:4)) -[nii.hdr.hist.originator(1:3).*nii.hdr.dime.pixdim(2:4)]'];[0 0 0 1]];
      save([fileprefix '.mat'], 'M');
   end

   return					% save_nii

%-----------------------------------------------------------------------------------
function write_nii(nii, filetype, fileprefix, old_RGB)

   hdr = nii.hdr;

   if isfield(nii,'ext') && ~isempty(nii.ext)
      ext = nii.ext;
      [ext, esize_total] = verify_nii_ext(ext);
   else
      ext = [];
   end

   switch double(hdr.dime.datatype),
   case   1,
      hdr.dime.bitpix = int16(1 ); precision = 'ubit1';
   case   2,
      hdr.dime.bitpix = int16(8 ); precision = 'uint8';
   case   4,
      hdr.dime.bitpix = int16(16); precision = 'int16';
   case   8,
      hdr.dime.bitpix = int16(32); precision = 'int32';
   case  16,
      hdr.dime.bitpix = int16(32); precision = 'float32';
   case  32,
      hdr.dime.bitpix = int16(64); precision = 'float32';
   case  64,
      hdr.dime.bitpix = int16(64); precision = 'float64';
   case 128,
      hdr.dime.bitpix = int16(24); precision = 'uint8';
   case 256
      hdr.dime.bitpix = int16(8 ); precision = 'int8';
   case 511,
      hdr.dime.bitpix = int16(96); precision = 'float32';
   case 512
      hdr.dime.bitpix = int16(16); precision = 'uint16';
   case 768
      hdr.dime.bitpix = int16(32); precision = 'uint32';
   case 1024
      hdr.dime.bitpix = int16(64); precision = 'int64';
   case 1280
      hdr.dime.bitpix = int16(64); precision = 'uint64';
   case 1792,
      hdr.dime.bitpix = int16(128); precision = 'float64';
   otherwise
      error('This datatype is not supported');
   end

   hdr.dime.glmax = round(double(max(nii.img(:))));
   hdr.dime.glmin = round(double(min(nii.img(:))));

   if filetype == 2
      fid = fopen(sprintf('%s.nii',fileprefix),'w');

      if fid < 0,
         msg = sprintf('Cannot open file %s.nii.',fileprefix);
         error(msg);
      end

      hdr.dime.vox_offset = 352;

      if ~isempty(ext)
         hdr.dime.vox_offset = hdr.dime.vox_offset + esize_total;
      end

      hdr.hist.magic = 'n+1';
      save_nii_hdr(hdr, fid);

      if ~isempty(ext)
         save_nii_ext(ext, fid);
      end
   else
      fid = fopen(sprintf('%s.hdr',fileprefix),'w');

      if fid < 0,
         msg = sprintf('Cannot open file %s.hdr.',fileprefix);
         error(msg);
      end

      hdr.dime.vox_offset = 0;
      hdr.hist.magic = 'ni1';
      save_nii_hdr(hdr, fid);

      if ~isempty(ext)
         save_nii_ext(ext, fid);
      end

      fclose(fid);
      fid = fopen(sprintf('%s.img',fileprefix),'w');
   end

   ScanDim = double(hdr.dime.dim(5));		% t
   SliceDim = double(hdr.dime.dim(4));		% z
   RowDim   = double(hdr.dime.dim(3));		% y
   PixelDim = double(hdr.dime.dim(2));		% x
   SliceSz  = double(hdr.dime.pixdim(4));
   RowSz    = double(hdr.dime.pixdim(3));
   PixelSz  = double(hdr.dime.pixdim(2));

   x = 1:PixelDim;

   if filetype == 2 && isempty(ext)
      skip_bytes = double(hdr.dime.vox_offset) - 348;
   else
      skip_bytes = 0;
   end

   if double(hdr.dime.datatype) == 128

      %  RGB planes are expected to be in the 4th dimension of nii.img
      %
      if(size(nii.img,4)~=3)
         error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']);
      end

      if old_RGB
         nii.img = permute(nii.img, [1 2 4 3 5 6 7 8]);
      else
         nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]);
      end
   end

   if double(hdr.dime.datatype) == 511

      %  RGB planes are expected to be in the 4th dimension of nii.img
      %
      if(size(nii.img,4)~=3)
         error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']);
      end

      if old_RGB
         nii.img = permute(nii.img, [1 2 4 3 5 6 7 8]);
      else
         nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]);
      end
   end

   %  For complex float32 or complex float64, voxel values
   %  include [real, imag]
   %
   if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792
      real_img = real(nii.img(:))';
      nii.img = imag(nii.img(:))';
      nii.img = [real_img; nii.img];
   end

   if skip_bytes
      fwrite(fid, zeros(1,skip_bytes), 'uint8');
   end

   fwrite(fid, nii.img, precision);
%   fwrite(fid, nii.img, precision, skip_bytes);        % error using skip
   fclose(fid);

   return;					% write_nii


function output = condenseFields(data, regexp, nullData)
% "condenseFields"
%   Search a datastructure (data) recursively for instances of a fieldname
%   expression (regexp).  If the fields matching the regexp are encountered,
%   concatenate them together, creating an array, and replace the parent of
%   this list with the array.  New empty fields are filled with nullData if
%   it is specified, else they are [].
%   See dissimilarInsert for details on the concatenation.
%
%   JRA 10/28/03
%
% Warning:  This function must be used carefully, it can result in dataloss
% if you do not know beforehand how your data is structured.
%
% For DICOM plans with Item_1, Item_2, ... use 'Item_\d' as the regexp.
%
% Usage:
%   function output = condenseFields(data, regexp, nullData)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

if ~exist('nullData')
    nullData = [];
end
dataClass = class(data);

switch dataClass

    case 'struct'
		myFields = fields(data);
		for i=1:length(myFields)
            for j=1:length(data)
                if isstruct(data(j).(myFields{i}))
                    data(j).(myFields{i}) = condenseFields(data(j).(myFields{i}), regexp, nullData);
                end
            end
		end
		matches = regexpi(myFields, regexp);
		if length(matches) == 1;
            matches = {matches};
		end
		index = 1;
		for i=1:length(matches)
            if matches{i} == 1
                if ~exist('temp')
                    temp(index) = data.(myFields{i});
                else
                    temp = dissimilarInsert(temp, data.(myFields{i}), index, nullData);
                end
                index = index+1;
            end
		end
		if exist('temp')
            output = temp;
		else
            output = data;
		end

    case 'cell'
        numCells = length(data);
        for i=1:numCells
            data{i} = condenseFields(data{i}, regexp, nullData);
        end
        output = data;
end


tracker_label = 'Demo_Cpp';

% Example is stored here: <path-to-toolkit-dir>\tracker\examples\native
tracker_command = '<path-to-build-directory>\static_cpp_rgbd.exe';

% tracker_interpreter = []; % Set the interpreter used here as a lower case string. E.g. if you are using Matlab, write 'matlab'. (optional)

tracker_linkpath = {'<path-to-trax-build-dir>\bin', ...
    '<path-to-trax-build-dir>\lib'};


function idx = gui_select_bracketed(listhandle)
% make sure that the user does not select [...] entries (except if not otherwise possible)

contents = cellstr(get(listhandle,'String'));
idx = get(listhandle,'Value');
for k=idx-1:idx-1+length(contents)
    idx = mod(k,length(contents))+1;
    set(listhandle,'Value',idx)
    sel = contents{idx};
    if ~isempty(sel) && sel(1) ~= '['
        break; end
end


function unzipAll(varargin)

% function unzipAll(pathName,pathNameOutput)
% ------------------------------------------------------------------------
%
% This function unzips all zip files in the folder pathName. The first
% input is the folder path name, the following inputs are possible inputs
% of the unzip function.
%
% See also: dicomanon
%
% Kevin Mattheus Moerman
% gibbon.toolbox@gmail.com
%
% 2018/02/09: Created
%------------------------------------------------------------------------

%%
switch nargin
    case 1
        pathName=varargin{1};
        varOutput=pathName;
    case 2
        pathName=varargin{1};
        varOutput=varargin{2};
end
%%

if ischar(varOutput) %Process only input folder
    files = dir(fullfile(pathName,'*.zip'));
    files={files(1:end).name};
    files=sort(files(:));
    if ~isempty(files)
        if numel(files)>1
            hw = waitbar(0,'Unzipping files...');
            for q=1:1:numel(files)
                fileName=fullfile(pathName,files{q});
                unzip(fileName,varOutput);
                waitbar(q/numel(files),hw,['Unzipping files...',num2str(round(100.*q/numel(files))),'%']);
            end
            close(hw)
        else
            fileName=fullfile(pathName,files{1});
            unzip(fileName,varOutput);
        end
    end
else %Process input folder and all subfolders
    %Process current
    unzipAll(pathName,pathName);

    %Process sub-folders
    [pathNames]=getSubPaths(pathName);
    if ~isempty(pathNames)
        for q=1:1:numel(pathNames)
            pathNameNow=pathNames{q};
            unzipAll(pathNameNow,pathNameNow);
        end
    end
end

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


function res = transpose(obj);

    res = ctranspose(obj, 0);


function ft_progress(varargin)

% FT_PROGRESS shows a graphical or non-graphical progress indication similar to the
% standard WAITBAR function, but with the extra option of printing it in the command
% window as a plain text string or as a rotating dial. Alternatively, you can also
% specify it not to give feedback on the progress.
%
% Prior to the for-loop, you should call either
%   ft_progress('init', 'none',    'Please wait...')
%   ft_progress('init', 'text',    'Please wait...')
%   ft_progress('init', 'textbar', 'Please wait...')      % ascii progress bar
%   ft_progress('init', 'dial',    'Please wait...')      % rotating dial
%   ft_progress('init', 'etf',     'Please wait...')      % estimated time to finish
%   ft_progress('init', 'gui',     'Please wait...')
%
% In each iteration of the for-loop, you should call either
% ft_progress(x)                                          % only show percentage
% ft_progress(x, 'Processing event %d from %d', i, N)     % show string, x=i/N
%
% After finishing the for-loop, you should call
%   ft_progress('close')
%
% Here is an example for the use of a progress indicator
%   ft_progress('init', 'etf', 'Please wait...');
%   for i=1:100
%     ft_progress(i/100, 'Processing event %d from %d', i, 100);
%     pause(0.03);
%   end
%   ft_progress('close')
%
% See also WAITBAR

% Copyright (C) 2004-2022, 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$

persistent p         % the previous value of the progress
persistent c         % counter for the number of updates that is done
persistent h         % the handle of the dialog (in case of type=gui)
persistent t0        % initial time, required for ETF
persistent p0        % initial percentage, required for ETF
persistent type      % type of feedback, string with none, gui, text, textcr, textnl
persistent title     % the string containing the title
persistent angle     % the angle in degrees, for dial or textbar
persistent strlen    % the length of the previously printed string, used to remove it by \b
persistent stopwatch % the time of previous invocation, used to restrict number of updates
persistent closing
persistent lastargin

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if nargin>1 && ischar(varargin{1}) && strcmp(varargin{1}, 'init')
  % reset these to the defaults
  p = 0;
  c = 0;
  h = 0;
  t0 = [];
  p0 = [];
  type = 'none'; % set below
  title = ''; % set below
  angle = 0;
  strlen = 0;
  stopwatch = tic();
  lastargin = [];
  closing = false;

  % determine the type of feedback
  type = varargin{2};
  if strcmp(type, 'textcr') || strcmp(type, 'textnl')
    type = 'text';
  end
  % determine the title of the dialog
  if nargin>2
    title = varargin{3};
  else
    title = '';
  end
  switch type
    case 'gui'
      % initialise the waitbar dialog
      if ~isempty(title)
        h = waitbar(0, title);
      else
        h = waitbar(0, 'Please wait');
      end
    case {'text', 'textnl', 'textcr'}
      if ~isempty(title)
        % print the title to the screen and go to the next line
        fprintf('%s\n', title)
      end
  end

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
elseif nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'close')

  if ~isempty(lastargin)
    % the last input argument is used when ft_progress('close') is called but the
    % previous invocation was not processed yet due to the restriction in the number of
    % updates to once every 100 ms
    closing = true;
    ft_progress(lastargin{:});
  end

  switch type
    case 'gui'
      % close the waitbar dialog
      close(h);
    case {'text', 'etf', 'dial', 'textbar'}
      % finish by going to the next line
      fprintf('\n');
  end

  % reset these to the defaults
  p = 0;
  c = 0;
  h = 0;
  t0 = [];
  p0 = [];
  type = 'none';
  title = '';
  angle = 0;
  strlen = 0;
  stopwatch = [];
  lastargin = [];
  closing = false;

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
else

  % make sure we don't update more than once every 100 ms, otherwise certain
  % conditions result in a significant performance hit
  if ~isempty(stopwatch) && toc(stopwatch) < 0.1 && ~closing
    lastargin = varargin;
    return;
  end
  stopwatch = tic();
  lastargin = [];

  if strcmp(type, 'dial')
    % display should always be updated for the dial
  elseif (varargin{1}-p)<0.01 && strcmp(type, 'gui')
    % display should not be updated it the difference is less than one percent
    return;
  elseif (varargin{1}-p)<0.01 && strcmp(type, 'textbar')
    % display should not be updated it the difference is less than one percent
    return;
  elseif (varargin{1}-p)<0.01 && strcmp(type, 'etf')
    % display should not be updated it the difference is less than one percent
    return;
  end

  % count the number of updates, for debugging
  c = c+1;

  % remember the current value for the next function call
  p = varargin{1};

  switch type
    case 'gui'
      % update the the length of the bar in the waitbar dialog
      waitbar(varargin{1}, h);

    case 'etf'
      % compute the estimated time that the computation still needs to finish
      if isempty(t0) || isempty(p0)
        t0 = clock;
        p0 = p;
      end
      elapsed = etime(clock, t0);
      if nargin>1 && ~isempty(varargin{2})

        varargin{2} = [repmat(sprintf('\b'), [1 strlen]) varargin{2}];

        part1 = sprintf(varargin{2:end});
        part2 = sprintf(' - estimated time to finish is %d seconds', round(elapsed*(1-p)/(p-p0)));

        fprintf([part1 part2]);

        % record actual string length that was printed (subtracting all the \b's)
        strlen = length(part1) + length(part2) - strlen;
      else
        % only print the estimated time to finish
        part1 = sprintf([repmat(sprintf('\b'), [1 strlen]) ' - estimated time to finish is %d seconds'], round(elapsed*(1-p)/(p-p0)));
        fprintf(part1);
        strlen = length(part1) - strlen + 1;
      end

    case 'dial'
      dial = '|/-\|/-\';
      if ~isempty(title)
        % print the title and draw a new hand of the rotating dial
        part1 = sprintf([repmat(sprintf('\b'), [1 strlen]) '%s %s'], title, dial(1+angle/45));
        strlen = length(title) + 2;
      else
        % draw a new hand of the rotating dial
        part1 = sprintf([repmat(sprintf('\b'), [1 strlen]) '%s'], dial(1+angle/45));
        strlen = 1;
      end
      if part1(end) == '\'
        % a single backslash is not valid, add another one
        part1(end+1) = '\';
      end
      fprintf(part1);
      % increment the angle with 45 degrees
      angle = angle + 45;
      if angle==360
        % reset the angle to 0 degrees
        angle = 0;
      end

    case 'textbar'
      dial = '|/-\|/-\';
      % construct the line looking like this [------/ ]
      len = 75 - length(title) - 3;
      len1 = round(p*len); % number of '-' characters before the dial
      len2 = len - len1; % number of ' ' characters after the dial
      line = [title, ' [' repmat('-', 1, len1), dial(1+angle/45), repmat(' ', 1, len2), ']'];
      if c~=1
        backline = repmat('\b', [1 length(line)]);
      else
        backline = '';
      end
      % don't use carriage return, it sometimes leads to a new line
      fprintf([backline '%s'], line);
      % increment the angle with 45 degrees
      angle = angle + 45;
      if angle==360
        % reset the angle to 0 degrees
        angle = 0;
      end

    case 'text'
      if nargin>1
        % ensure the string does not end with a newline or carriage return, since
        % either would break compatibility with a -nodesktop matlab environment
        if length(varargin{2})>1 && (all(varargin{2}((end-1):end) == '\r')...
            || all(varargin{2}((end-1):end) == '\n'))
          varargin{2} = varargin{2}(1:end-2);
        end

        varargin{2} = [repmat(sprintf('\b'), [1 strlen]) varargin{2}];
        if usejava('desktop')
          % a newline is appropriate when using the desktop environment
          varargin{2} = [varargin{2} '\n'];
        end

        part1 = sprintf(varargin{2:end});
        fprintf(part1);
        strlen = length(part1) - strlen;

      else
        part1 = sprintf([repmat(sprintf('\b'), [1 strlen]) '%6.2f %%'], 100*varargin{1});
        fprintf(part1);
        strlen = length(part1) - strlen;
      end

  end % case gui, etf, dial, textbar, text
end % updating the displayed value

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% some test code follows
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% ft_progress('init', 'gui'); for i=1:100, ft_progress(i/100, '%d/%d\r', i, 100); pause(0.03); end; ft_progress('close')
% ft_progress('init', 'textbar'); for i=1:100, ft_progress(i/100, '%d/%d\r', i, 100); pause(0.03); end; ft_progress('close')
% ft_progress('init', 'dial'); for i=1:100, ft_progress(i/100, '%d/%d\r', i, 100); pause(0.03); end; ft_progress('close')
% ft_progress('init', 'none'); for i=1:100, ft_progress(i/100, '%d/%d\r', i, 100); pause(0.03); end; ft_progress('close')

% ft_progress('init', 'text'); for i=1:100, ft_progress(i/100, '%d/%d' , i, 100); pause(0.03); end; ft_progress('close')
% ft_progress('init', 'text'); for i=1:100, ft_progress(i/100, '%d/%d\n', i, 100); pause(0.03); end; ft_progress('close')
% ft_progress('init', 'text'); for i=1:100, ft_progress(i/100, '%d/%d\r', i, 100); pause(0.03); end; ft_progress('close')

% ft_progress('init', 'textnl'); for i=1:100, ft_progress(i/100, '%d/%d' , i, 100); pause(0.03); end; ft_progress('close')
% ft_progress('init', 'textnl'); for i=1:100, ft_progress(i/100, '%d/%d\n', i, 100); pause(0.03); end; ft_progress('close')
% ft_progress('init', 'textnl'); for i=1:100, ft_progress(i/100, '%d/%d\r', i, 100); pause(0.03); end; ft_progress('close')

% ft_progress('init', 'textcr'); for i=1:100, ft_progress(i/100, '%d/%d' , i, 100); pause(0.03); end; ft_progress('close')
% ft_progress('init', 'textcr'); for i=1:100, ft_progress(i/100, '%d/%d\n', i, 100); pause(0.03); end; ft_progress('close')
% ft_progress('init', 'textcr'); for i=1:100, ft_progress(i/100, '%d/%d\r', i, 100); pause(0.03); end; ft_progress('close')


function this = load_data(this)
%ONE_LINE_DESCRIPTION
%
%   Y = MrRoi()
%   Y.load_data(inputs)
%
% This is a method of class MrRoi.
%
% IN
%
% OUT
%
% EXAMPLE
%   load_data
%
%   See also MrRoi

% Author:   Saskia Bollmann & Lars Kasper
% Created:  2014-11-21
% Copyright (C) 2014 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public Licence (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.
%

% get filename
filename = fullfile(this.parameters.save.path, this.parameters.save.fileName);
% load data
data = struct2cell(load(filename, 'data'));
this.data = data{:};


function QSARreadyCurated34STARDataPadelheader = importfile(filename, startRow, endRow)
%IMPORTFILE Import numeric data from a text file as a matrix.
%   QSARREADYCURATED34STARDATAPADELHEADER = IMPORTFILE(FILENAME) Reads data
%   from text file FILENAME for the default selection.
%
%   QSARREADYCURATED34STARDATAPADELHEADER = IMPORTFILE(FILENAME, STARTROW,
%   ENDROW) Reads data from rows STARTROW through ENDROW of text file
%   FILENAME.
%
% Example:
%   QSARreadyCurated34STARDataPadelheader =
%   importfile('QSAR_ready_Curated_3_4STAR_Data_Padel_header.csv', 1,
%   1444);
%
%    See also TEXTSCAN.

% Auto-generated by MATLAB on 2015/12/19 14:14:26

%% Initialize variables.
delimiter = '';
if nargin<=2
    startRow = 1;
    endRow = inf;
end

%% Format string for each line of text:
%   column1: text (%s)
% For more information, see the TEXTSCAN documentation.
formatSpec = '%s%[^\n\r]';

%% Open the text file.
fileID = fopen(filename,'r');

%% Read columns of data according to format string.
% This call is based on the structure of the file used to generate this
% code. If an error occurs for a different file, try regenerating the code
% from the Import Tool.
dataArray = textscan(fileID, formatSpec, endRow(1)-startRow(1)+1, 'Delimiter', delimiter, 'HeaderLines', startRow(1)-1, 'ReturnOnError', false);
for block=2:length(startRow)
    frewind(fileID);
    dataArrayBlock = textscan(fileID, formatSpec, endRow(block)-startRow(block)+1, 'Delimiter', delimiter, 'HeaderLines', startRow(block)-1, 'ReturnOnError', false);
    dataArray{1} = [dataArray{1};dataArrayBlock{1}];
end

%% Close the text file.
fclose(fileID);

%% Post processing for unimportable data.
% No unimportable data rules were applied during the import, so no post
% processing code is included. To generate code which works for
% unimportable data, select unimportable cells in a file and regenerate the
% script.

%% Create output variable
QSARreadyCurated34STARDataPadelheader = [dataArray{1:end-1}];


function xyl_data_write ( point_num, line_num, line_data_num, line_pointer, ...
  line_data )

%*****************************************************************************80
%
%% XYL_DATA_PRINT prints the data of an XYL file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 January 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer POINT_NUM, the number of points.
%
%    Input, integer LINE_NUM, the number of lines.
%
%    Input, integer LINE_DATA_NUM, the number of line items.
%
%    Input, integer LINE_POINTER(LINE_NUM+1), pointers to the
%    first line item for each line.
%
%    Input, integer LINE_DATA(LINE_DATA_NUM), indices
%    of points that form lines.
%
  fprintf ( 1, '\n' );
  for line = 1 : line_num
    fprintf ( 1, '  %4d  %8d  %8d\n', ...
      line,  line_pointer(line) : line_pointer(line+1) - 1 );
  end
  fprintf ( 1, '\n' );
  for line = 1 : line_num
    for i = line_pointer(line) : line_pointer(line+1) - 1
      fprintf ( 1, '  %d', line_data(i) );
    end
    fprintf ( 1, '\n' );
  end

  return
end


function [solver,problem,forced_choice,failureMode] = selectsolver(options,ProblemClass,solvers,socp_are_really_qc,allsolvers);
%SELECTSOLVER Internal function to select solver based on problem category

problem = 0;
failureMode = '';

% UNDOCUMENTED
force_solver = yalmip('solver');
if length(force_solver)>0
    options.solver = force_solver;
end

% YALMIP has discovered in an previous call that the model isn't a GP, and
% now searches for a non-GP solver
if options.thisisnotagp
    ProblemClass.gppossible = 0;
end

% ***************************************************
% Maybe the user is stubborn and wants to pick solver
% ***************************************************
forced_choice = 0;
if length(options.solver)>0 & isempty(strfind(options.solver,'*'))

    if strfind(options.solver,'+')
        forced_choice = 1;
        options.solver = strrep(options.solver,'+','');
    end
    % Create tags with version also
    temp = expandSolverName(solvers);

    opsolver = lower(options.solver);
    splits = strfind(opsolver,',');
    if isempty(splits)
        names{1} = opsolver;
    else
        start = 1;
        for i  = 1:length(splits)
            names{i} = opsolver(start:splits(i)-1);
            start = splits(i)+1;
        end
        names{end+1} = opsolver(start:end);
    end

    index1 = [];
    index2 = [];
    for i = 1:length(names)
        index1 = [index1 find(strcmpi({solvers.tag},names{i}))];
        index2 = [index1 find(strcmpi({temp.tag},names{i}))];
    end
    if isempty(index1) & isempty(index2)
        % Specified solver not found among available solvers
        % Is it even a supported solver
        temp = expandSolverName(allsolvers);
        for i = 1:length(names)
            index1 = [index1 find(strcmp(lower({allsolvers.tag}),names{i}))];
            index2 = [index1 find(strcmp(lower({temp.tag}),names{i}))];
        end
        if isempty(index1) & isempty(index2)
            problem = -9;
        else
            problem = -3;
        end
        solver = [];
        return;
    else
        solvers = solvers(union(index1,index2));
    end
end

% ************************************************
% Prune based on forced global solver
% ************************************************
if  ~forced_choice && options.forceglobal
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).global;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'global optimization');
end
% Prune based on avoiding global solver (in BMIBNB we don't want that as
% upper solver)
if isfield(options,'avoidGlobalSolver')
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = ~solvers(i).global;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'global optimization');
end

% ************************************************
% Prune based on objective
% ************************************************
if ProblemClass.objective.sigmonial & ~forced_choice & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).objective.sigmonial;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'signomial terms in objective');
end

% ************************************************
% Prune based on objective
% ************************************************
if ProblemClass.objective.sigmonial & ~forced_choice & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).objective.sigmonial;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'signomial terms in objective');
end
if ProblemClass.objective.polynomial & ~forced_choice & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).objective.polynomial | solvers(i).objective.sigmonial;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'polynomial terms in objective');
end
if ProblemClass.objective.quadratic.nonconvex & ~forced_choice & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).objective.polynomial | solvers(i).objective.sigmonial | solvers(i).objective.quadratic.nonconvex;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'nonconvex quadratic terms in objective');
end
if ProblemClass.objective.quadratic.convex & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        direct = solvers(i).objective.polynomial | solvers(i).objective.sigmonial | solvers(i).objective.quadratic.nonconvex | solvers(i).objective.quadratic.convex;
        indirect = solvers(i).constraint.inequalities.semidefinite.linear | solvers(i).constraint.inequalities.secondordercone.linear;
        if direct | indirect
            keep(i)=1;
        else
            keep(i)=0;
        end
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'quadratic terms in objective');
end
if ProblemClass.objective.linear & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).objective.polynomial | solvers(i).objective.sigmonial | solvers(i).objective.quadratic.nonconvex | solvers(i).objective.quadratic.convex | solvers(i).objective.linear;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'linear terms in objective');
end

if ProblemClass.objective.maxdet.convex & ~ProblemClass.objective.linear & ~ProblemClass.objective.quadratic.convex & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).objective.maxdet.convex | solvers(i).constraint.inequalities.semidefinite.linear;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'maxdet terms in objective');
end

if ProblemClass.objective.maxdet.convex & (ProblemClass.objective.linear | ProblemClass.objective.quadratic.convex) & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).objective.maxdet.convex;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'maxdet terms in objective');
end

if ProblemClass.objective.maxdet.nonconvex & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).objective.maxdet.nonconvex;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'nonconvex maxdet terms in objective');
end

% ******************************************************
% Prune based on rank constraints
% ******************************************************
if ProblemClass.constraint.inequalities.rank & ~forced_choice & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.rank;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'rank constraints');
end

% ******************************************************
% Prune based on semidefinite constraints
% ******************************************************
if ProblemClass.constraint.inequalities.semidefinite.sigmonial & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.semidefinite.sigmonial;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'signomial semidefinite constraints');
end
if ProblemClass.constraint.inequalities.semidefinite.polynomial & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.semidefinite.sigmonial |  solvers(i).constraint.inequalities.semidefinite.polynomial;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'polynomial semidefinite constraints');
end
if ProblemClass.constraint.inequalities.semidefinite.quadratic & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.semidefinite.sigmonial |  solvers(i).constraint.inequalities.semidefinite.polynomial | solvers(i).constraint.inequalities.semidefinite.quadratic;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'quadratic semidefinite constraints');
end
if ProblemClass.constraint.inequalities.semidefinite.linear & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.semidefinite.sigmonial |  solvers(i).constraint.inequalities.semidefinite.polynomial | solvers(i).constraint.inequalities.semidefinite.quadratic | solvers(i).constraint.inequalities.semidefinite.linear;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'semidefinite constraints');
end

% If user has specified a, e.g., LP solver for an SDP when using OPTIMIZER,
% we must bail out, as there is no chance this model instantiates as an LP.
if forced_choice &  (ProblemClass.constraint.inequalities.semidefinite.linear | ProblemClass.constraint.inequalities.semidefinite.quadratic | ProblemClass.constraint.inequalities.semidefinite.polynomial | ProblemClass.constraint.inequalities.semidefinite.sigmonial)  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.semidefinite.sigmonial |  solvers(i).constraint.inequalities.semidefinite.polynomial | solvers(i).constraint.inequalities.semidefinite.quadratic | solvers(i).constraint.inequalities.semidefinite.linear;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'semidefinite constraints');
end
% Similarily, we have a SOCP by definition. We must support that
if forced_choice & ~socp_are_really_qc & (ProblemClass.constraint.inequalities.secondordercone.linear | ProblemClass.constraint.inequalities.secondordercone.nonlinear)  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.secondordercone.linear;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'second-order cone constraints');
end

% ******************************************************
% Prune based on cone constraints
% ******************************************************
if ProblemClass.constraint.inequalities.secondordercone.linear & ~socp_are_really_qc & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
         keep(i) = solvers(i).constraint.inequalities.secondordercone.linear | solvers(i).constraint.inequalities.semidefinite.linear | solvers(i).constraint.inequalities.elementwise.quadratic.nonconvex;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'second-order cone constraints');
end
if ProblemClass.constraint.inequalities.secondordercone.nonlinear & ~socp_are_really_qc & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
         keep(i) = solvers(i).constraint.inequalities.secondordercone.nonlinear | solvers(i).constraint.inequalities.elementwise.quadratic.nonconvex;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'nonlinear second-order cone constraints');
end
if ProblemClass.constraint.inequalities.rotatedsecondordercone.linear & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.rotatedsecondordercone.linear | solvers(i).constraint.inequalities.secondordercone.linear | solvers(i).constraint.inequalities.semidefinite.linear;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'rotated second-order cone constraints');
end
if ProblemClass.constraint.inequalities.rotatedsecondordercone.nonlinear & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.rotatedsecondordercone.nonlinear | solvers(i).constraint.inequalities.secondordercone.linear | solvers(i).constraint.inequalities.semidefinite.linear;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'nonlinear rotated second-order cone constraints');
end
% if ProblemClass.constraint.inequalities.powercone && ~forced_choice  && length(solvers)>0
%     keep = ones(length(solvers),1);
%     for i = 1:length(solvers)
%         keep(i) = solvers(i).powercone;
%     end
%     [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'power cone constraints');
% end

% ******************************************************
% Prune based on element-wise inequality constraints
% ******************************************************
if ProblemClass.constraint.inequalities.elementwise.sigmonial & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.elementwise.sigmonial;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'signomial constraints');
end
if ProblemClass.constraint.inequalities.elementwise.polynomial & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.elementwise.sigmonial |  solvers(i).constraint.inequalities.elementwise.polynomial;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'polynomial constraints');
end
if ProblemClass.constraint.inequalities.elementwise.quadratic.nonconvex & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.elementwise.sigmonial |  solvers(i).constraint.inequalities.elementwise.polynomial | solvers(i).constraint.inequalities.elementwise.quadratic.nonconvex;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'nonconvex quadratic constraints');
end
if ProblemClass.constraint.inequalities.elementwise.quadratic.convex | (ProblemClass.constraint.inequalities.secondordercone.linear & socp_are_really_qc) & ~forced_choice & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.elementwise.sigmonial |  solvers(i).constraint.inequalities.elementwise.polynomial | solvers(i).constraint.inequalities.elementwise.quadratic.nonconvex | solvers(i).constraint.inequalities.elementwise.quadratic.convex | solvers(i).constraint.inequalities.secondordercone.linear | solvers(i).constraint.inequalities.semidefinite.linear;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'quadratic constraints');
end
if ProblemClass.constraint.inequalities.elementwise.linear & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.elementwise.sigmonial |  solvers(i).constraint.inequalities.elementwise.polynomial | solvers(i).constraint.inequalities.semidefinite.quadratic | solvers(i).constraint.inequalities.elementwise.linear;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'constraints');
end

% ******************************************************
% Prune based on element-wise constraints
% ******************************************************
if ProblemClass.constraint.equalities.sigmonial & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.inequalities.elementwise.sigmonial | solvers(i).constraint.equalities.sigmonial;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'signomial equality constraints');
end
if ProblemClass.constraint.equalities.polynomial & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        indirect = solvers(i).constraint.inequalities.elementwise.sigmonial | solvers(i).constraint.inequalities.elementwise.polynomial;
        indirect = indirect | solvers(i).constraint.inequalities.elementwise.sigmonial | solvers(i).constraint.inequalities.elementwise.polynomial;
        direct = solvers(i).constraint.equalities.sigmonial | solvers(i).constraint.equalities.polynomial;
        keep(i) = direct | indirect;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'polynomial equality constraints');
end
if ProblemClass.constraint.equalities.quadratic & ~forced_choice & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        indirect = solvers(i).constraint.inequalities.elementwise.sigmonial | solvers(i).constraint.inequalities.elementwise.polynomial | solvers(i).constraint.inequalities.elementwise.quadratic.nonconvex;
        direct = solvers(i).constraint.equalities.sigmonial |  solvers(i).constraint.equalities.polynomial |  solvers(i).constraint.equalities.quadratic;
        keep(i) = direct | indirect;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'quadratic equality constraints');
end
if ProblemClass.constraint.equalities.linear & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        indirect = solvers(i).constraint.inequalities.elementwise.linear | solvers(i).constraint.inequalities.elementwise.sigmonial |  solvers(i).constraint.inequalities.elementwise.polynomial;
        direct = solvers(i).constraint.equalities.linear | solvers(i).constraint.equalities.sigmonial |  solvers(i).constraint.equalities.polynomial;
        keep(i) = direct | indirect;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'equality constraints');
end

% ******************************************************
% Discrete data
% ******************************************************
if ProblemClass.constraint.integer & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).constraint.integer;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'integer variables');
end
if ProblemClass.constraint.binary & ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
         keep(i) = solvers(i).constraint.integer | solvers(i).constraint.binary;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'binary variables');
end
if ProblemClass.constraint.sos1 & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
         %keep(i) = solvers(i).constraint.integer | solvers(i).constraint.binary | solvers(i).constraint.sos2;
         keep(i) = solvers(i).constraint.sos2;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'sos1 constraints');
end
if ProblemClass.constraint.sos2 & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
         keep(i) = solvers(i).constraint.integer | solvers(i).constraint.binary | solvers(i).constraint.sos2;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'sos2 constraints');
end
if ProblemClass.constraint.semicont  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
         %keep(i) = solvers(i).constraint.integer | solvers(i).constraint.binary | solvers(i).constraint.sos2;
         keep(i) = solvers(i).constraint.semivar;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'semicontinuous variables');
end

% ******************************************************
% Equalities with multiple monomoials (rule out GP)
% ******************************************************
if ProblemClass.constraint.equalities.multiterm  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
         keep(i) = solvers(i).constraint.equalities.multiterm;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'multi-term monomial equalities');
end
% FIXME
% No support for multiterm is YALMIPs current way of saying "GP solver". We
% use this flag to prune GPs based on objective too
if ~ProblemClass.gppossible  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
         keep(i) = solvers(i).constraint.equalities.multiterm;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'multi-term monomial equalities');
end

% ******************************************************
% Complementarity constraints
% ******************************************************
if ProblemClass.constraint.complementarity.linear & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
         keep(i) = solvers(i).constraint.complementarity.linear | solvers(i).constraint.integer | solvers(i).constraint.binary | solvers(i).constraint.equalities.polynomial | solvers(i).constraint.equalities.quadratic;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'complementarity constraints');
end

% ******************************************************
% Interval data
% ******************************************************
if ProblemClass.interval  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = solvers(i).interval;
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'interval data');
end

% ******************************************************
% Parametric problem
% ******************************************************
if ~forced_choice  & length(solvers)>0
    keep = ones(length(solvers),1);
    for i = 1:length(solvers)
        keep(i) = (ProblemClass.parametric == solvers(i).parametric);
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'multiparametric models');
end

% ******************************************************
% Exponential cone representable (exp, log,...)
% ******************************************************
keep = ones(length(solvers),1);
if ~forced_choice  & length(solvers)>0
    for i = 1:length(solvers)
        keep(i) = (ProblemClass.exponentialcone <= solvers(i).exponentialcone) || (ProblemClass.exponentialcone <= solvers(i).evaluation);
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'exponential cones');
end
% ******************************************************
% Power cone representable
% ******************************************************
keep = ones(length(solvers),1);
if ~forced_choice  & length(solvers)>0
    for i = 1:length(solvers)
        keep(i) = (ProblemClass.constraint.inequalities.powercone <= solvers(i).powercone) || (ProblemClass.constraint.inequalities.powercone <= solvers(i).evaluation);
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'exponential cones');
end

% ******************************************************
% General functions (sin, cos,...)
% ******************************************************
keep = ones(length(solvers),1);
if ~forced_choice  & length(solvers)>0
    for i = 1:length(solvers)
        keep(i) = (ProblemClass.evaluation <= solvers(i).evaluation) || (ProblemClass.exponentialcone && solvers(i).exponentialcone);
    end
    [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,'general nonlinearities');
end

% FIX : UUUUUUGLY
if isempty(solvers)
    solver = [];
else
    if length(options.solver)>0
        solver = [];

        % FIX : Re-use from above
        opsolver = lower(options.solver);
        splits = strfind(opsolver,',');
        if isempty(splits)
            names{1} = opsolver;
        else
            names = {};
            start = 1;
            for i  = 1:length(splits)
                names{i} = opsolver(start:splits(i)-1);
                start = splits(i)+1;
            end
            names{end+1} = opsolver(start:end);
        end

        temp = solvers;
        for i = 1:length(temp)
        if length(temp(i).version)>0
            temp(i).tag = lower([temp(i).tag '-' temp(i).version]);
        end
        end

        for i = 1:length(names)
            if isequal(names{i},'*')
                solver = solvers(1);
                break
            else
                j = find(strcmpi(lower({solvers.tag}),names{i}));
                if ~isempty(j)
                    solver = solvers(j(1));
                    break
                end
                j = find(strcmpi(lower({temp.tag}),names{i}));
                if ~isempty(j)
                    solver = solvers(j(1));
                    break
                end
            end
        end
    else
        solver = solvers(1);
    end
end

if isempty(solver)
    if length(options.solver)>0 % User selected available solver, but it is not applicable
        problem = -4;
    else
        problem = -2;
    end
end

% FIX : Hack when chosing the wrong fmincon thingy
if ~isempty(solver)
    c1 = (length(options.solver)==0 | isequal(lower(options.solver),'fmincon')) & isequal(lower(solver.tag),'fmincon') & isequal(solver.version,'geometric');
    c2 = (length(options.solver)==0 | isequal(lower(options.solver),'snopt')) & isequal(lower(solver.tag),'snopt') & isequal(solver.version,'geometric');
    if c1 | c2
        if ~(ProblemClass.objective.sigmonial | ProblemClass.constraint.inequalities.elementwise.sigmonial)
            solver.version = 'standard';
            solver.call    = strrep(solver.call,'gp','');
            solver.objective.linear = 1;
            solver.objective.quadratic.convex = 1;
            solver.objective.quadratic.nonconvex = 1;
            solver.objective.polynomial = 1;
            solver.objective.sigmonial = 1;
            solver.constraint.equalities.elementwise.linear = 1;
            solver.constraint.equalities.elementwise.quadratic.convex = 1;
            solver.constraint.equalities.elementwise.quadratic.nonconvex = 1;
            solver.constraint.equalities.elementwise.polynomial = 1;
            solver.constraint.equalities.elementwise.sigmonial = 1;
            solver.constraint.inequalities.elementwise.linear = 1;
            solver.constraint.inequalities.elementwise.quadratic.convex = 1;
            solver.constraint.inequalities.elementwise.quadratic.nonconvex = 1;
            solver.constraint.inequalities.elementwise.polynomial = 1;
            solver.constraint.inequalities.elementwise.sigmonial = 1;
            solver.constraint.inequalities.secondordercone.linear = 1;
            solver.constraint.inequalities.semidefinite.linear = 1;
            solver.constraint.inequalities.semidefinite.quadratic = 1;
            solver.constraint.inequalities.semidefinite.polynomial = 1;
            solver.constraint.inequalities.semidefinite.nonlinear = 1;
            solver.exponentialcone = 1;
            solver.powercone = 1;
            solver.dual = 1;
            solver.evaluation = 1;
        end
    end
end

function temp = expandSolverName(temp)
for i = 1:length(temp)
    if length(temp(i).version)>0
        temp(i).tag = lower([temp(i).tag '-' temp(i).version]);
    end
end

function [solvers,failureMode] = pruneBasedOn(solvers,keep,failureMode,reason)

solvers = solvers(find(keep));
if ~any(keep)
    failureMode = reason;
end


function fieldstring=datastructure(structvar, substructvar, filename)
%Display nested structures in 'structvar'; 'substructvar' is used in
%      recursion, must be [] in main call. Result string is saved in
%      file 'filename'
%filename: filename to use for output
%fieldstring: output (with \n linefeeds)
%example call datastructure(saliencyData,[],'saliencyData.txt')

%store format spacing, set compact:
fs=get(0,'FormatSpacing');
format compact

%lines and double lines for display:
lines=repmat('-',1,25);
dlines=repmat('=',1,25);

%get name of variable from call
if exist('substructvar') && ~isempty(substructvar)
    fieldstring = [substructvar '\n' lines '\n'];
else
    fieldstring = [inputname(1) '\n' lines '\n'];
end

if isstruct(structvar)
    %display main structure at current level of recursion:
    fieldstring=[fieldstring ' Struct \n' evalc('disp(structvar)')];
    fieldstring=[fieldstring dlines '\n'];
    %get fields names at current level of recursion:
    fields=fieldnames(structvar);
    for k=1:length(fields)
        if isstruct(eval(['structvar.' fields{k}]))
            %recursive call to get substructures:
            fieldstring=[fieldstring datastructure(eval(['structvar.' fields{k}]),fields{k})];
        end
    end
end
%save result
if exist('filename')
     % in main call
     fid=fopen(filename,'w');
     fprintf(fid, fieldstring);
     fclose(fid);
end

%rstore format spacing
set(0,'FormatSpacing',fs);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright 2014 National Renewable Energy Laboratory and National
% Technology & Engineering Solutions of Sandia, LLC (NTESS).
% Under the terms of Contract DE-NA0003525 with NTESS,
% the U.S. Government retains certain rights in this software.
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% wecSimTest runs WEC-Sim continuous integration testing suite.
%
%   results = wecSimTest returns a matlab.unittest.TestResult object
%
%   results = wecSimTest(..., NAME1, VALUE1, NAME2, VALUE2, ...) returns
%   a matlab.unittest.TestResult object, depending on the values of the
%   optional parameter name/value pairs. See Parameters below.
%
%   Parameters
%   ----------
%   'bemioTest'       Run tests for BEMIO. Default is true.
%
%   'regressionTest'  Run regression tests. These tests check if values
%                     have changed from a previous run. Default is true.
%
%   'compilationTest' Run compilation tests. These tests do not check
%                     correctness of the results. Default is true.
%
%   'runFromSimTest'  Run tests for execution of WECSim from Simulink.
%                     Default is true.
%
%   'rotationTest'    Run rotation tests. Default is true.
%
%   Users should also run the appropriate applications tests when
%   creating a PR into the WEC-Sim repository.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function results = wecSimTest(options)
    arguments
        options.bemioTest = true
        options.regressionTest = true
        options.compilationTest = true
        options.runFromSimTest = true
        options.rotationTest = true
    end
    % Import MATLAB unittest
    import matlab.unittest.TestSuite
    import matlab.unittest.Test
    import matlab.unittest.TestRunner
    import matlab.unittest.plugins.DiagnosticsRecordingPlugin
    import matlab.unittest.plugins.CodeCoveragePlugin
    % Define the tests
    suites = Test.empty();
    if options.bemioTest
        suites = [suites TestSuite.fromFile('tests/bemioTest.m')];
    end
    if options.regressionTest
        suites = [suites TestSuite.fromFile('tests/regressionTest.m')];
    end
    if options.runFromSimTest
        suites = [suites TestSuite.fromFile('tests/runFromSimTest.m')];
    end
    if options.rotationTest
        suites = [suites TestSuite.fromFile('tests/rotationTest.m')];
    end
    % Create TestRunner
    runner = TestRunner.withTextOutput; % Contains TestRunProgressPlugin, DiagnosticsOutputPlugin
    runner.addPlugin(DiagnosticsRecordingPlugin);
    runner.addPlugin(CodeCoveragePlugin.forFolder('./source','IncludingSubfolders',true));
    % Run the tests
    results = runner.run(suites);
    results.table
end