function planC = generate_DICOM_UID_Relationships(planC,structRefForC)
%"generate_DICOM_UID_Relationships"
%   Creates new fields in the passed planC containing the UIDs required for
%   DICOM export.  These fields are not a permenant part of the planC, so
%   this function should NOT be called on a global planC so that the new
%   fields are destroyed upon return.
%
%   As additional export capabilities are added, new UID fields may need to
%   be defined in this function.
%
%JRA 07/05/06
%
%Usage:
%   planC = generate_DICOM_UID_Relationships(planC)
%
% 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/>.

% Organization root for DOCOM UIDs
orgRoot = '1.3.6.1.4.1.9590.100.1.2';

% Initialize Series Number
seriesNumber = 7700;

% Read CERROptions.json
optS = getCERROptions();

indexS = planC{end};

%Generate a master study UID for all parts of this planC.
%Study_Instance_UID = dicomuid;
%if isfield(planC{indexS.scan}(1).scanInfo(1),'DICOMHeaders') && ...
%            ~isempty(planC{indexS.scan}(1).scanInfo(1).DICOMHeaders)
if isfield(planC{indexS.scan}(1).scanInfo(1),'studyInstanceUID') && ...
        ~isempty(planC{indexS.scan}(1).scanInfo(1).studyInstanceUID)
    Study_Instance_UID = planC{indexS.scan}(1).scanInfo(1).studyInstanceUID;
else
    %Study_Instance_UID = dicomuid;
    Study_Instance_UID = javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot);
end

if isfield(planC{indexS.scan}(1).scanInfo(1),'patientID') && ...
        ~isempty(planC{indexS.scan}(1).scanInfo(1).patientID)
    Patient_ID = planC{indexS.scan}(1).scanInfo(1).patientID;
else
    %Patient_ID = dicomuid;
    Patient_ID = javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot);
end

if isfield(planC{indexS.scan}(1).scanInfo(1),'studyDate') && ...
        ~isempty(planC{indexS.scan}(1).scanInfo(1).studyDate)
    Study_Date = planC{indexS.scan}(1).scanInfo(1).studyDate;
else
    Study_Date = datestr(now,'yyyymmdd');
end

if isfield(planC{indexS.scan}(1).scanInfo(1),'studyTime') && ...
        ~isempty(planC{indexS.scan}(1).scanInfo(1).studyTime)
    Study_Time = planC{indexS.scan}(1).scanInfo(1).studyTime;
else
    Study_Time = datestr(now,'hhmmss');
end

if isfield(planC{indexS.scan}(1).scanInfo(1),'patientName') && ...
        ~isempty(planC{indexS.scan}(1).scanInfo(1).patientName)
    patientName = planC{indexS.scan}(1).scanInfo(1).patientName;
else
    patientName = '';
end

if isfield(planC{indexS.scan}(1).scanInfo(1),'patientBirthDate') && ...
        ~isempty(planC{indexS.scan}(1).scanInfo(1).patientBirthDate)
    Patient_Birth_Date = planC{indexS.scan}(1).scanInfo(1).patientBirthDate;
else
    Patient_Birth_Date = '';
end

%% SCAN UIDs
%Iterate over CT scans
for i = 1:length(planC{indexS.scan})

    %Set the study instance UID.
    planC{indexS.scan}(i).Patient_ID = Patient_ID;

    %Set patient birth date.
    planC{indexS.scan}(i).Patient_Birth_Date = Patient_Birth_Date;

    %Set the study instance UID.
    planC{indexS.scan}(i).Study_Instance_UID = Study_Instance_UID;

    %Set the study date
    planC{indexS.scan}(i).Study_Date = Study_Date;

    %Set the study time
    planC{indexS.scan}(i).Study_Time = Study_Time;

    %Set the series number
    seriesNumber = seriesNumber + 1;
    planC{indexS.scan}(i).SeriesNumber = seriesNumber;

    %Generate a series instance UID for each scan;
    %planC{indexS.scan}(i).Series_Instance_UID = dicomuid;
    %if isfield(planC{indexS.scan}(i).scanInfo(1), 'DICOMHeaders') && ...
    %        ~isempty(planC{indexS.scan}(i).scanInfo(1).DICOMHeaders)
    if strcmpi(optS.retainOriginalUIDonExport,'yes') && ...
            isfield(planC{indexS.scan}(i).scanInfo(1),'seriesInstanceUID') && ...
            ~isempty(planC{indexS.scan}(i).scanInfo(1).seriesInstanceUID)
        planC{indexS.scan}(i).Series_Instance_UID = ...
            planC{indexS.scan}(i).scanInfo(1).seriesInstanceUID;
    else
        Series_Instance_UID = javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot);
        %planC{indexS.scan}(i).Series_Instance_UID = dicomuid;
        planC{indexS.scan}(i).Series_Instance_UID = Series_Instance_UID;
    end

    %Generate a frame of reference UID for each scan.
    %planC{indexS.scan}(i).Frame_Of_Reference_UID = dicomuid;
    %if isfield(planC{indexS.scan}(i).scanInfo(1), 'DICOMHeaders') && ...
    %        ~isempty(planC{indexS.scan}(i).scanInfo(1).DICOMHeaders)
    if strcmpi(optS.retainOriginalUIDonExport,'yes') && ...
            isfield(planC{indexS.scan}(i).scanInfo(1),'frameOfReferenceUID') && ...
            ~isempty(planC{indexS.scan}(i).scanInfo(1).frameOfReferenceUID)
        planC{indexS.scan}(i).Frame_Of_Reference_UID = ...
            planC{indexS.scan}(i).scanInfo(1).frameOfReferenceUID;
    else
        Frame_Of_Reference_UID = char(javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot));
        planC{indexS.scan}(i).Frame_Of_Reference_UID = Frame_Of_Reference_UID;
        %planC{indexS.scan}(i).Frame_Of_Reference_UID = dicomuid;
    end

    % %     %Generate a SOP ClassUID for each scan.
    % %     modality = planC{indexS.scan}(i).scanInfo(1).imageType;
    % %
    % %     switch lower(modality)
    % %         case {'ct scan'}
    % %             SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.2';
    % %         case {'mr scan'}
    % %             SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.4';
    % %         case {'pet scan'}
    % %             SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.129';
    % %         otherwise
    % %             %wy error('Currently unsupported scanType.  Only CT Scan is supported.');
    % %             SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.2';
    % %     end

    if ~isempty(planC{indexS.scan}(i).scanInfo(1).imageType)
        modality =planC{indexS.scan}(i).scanInfo(1).imageType;
    else
        modality = planC{indexS.scan}.scanInfo(1).imageType;
    end

    if any(strfind(upper(modality), 'CT'))
        SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.2';

    elseif any(strfind(upper(modality), 'MR'))
        SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.4';

    else
        SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.2';
    end

    %Iterate over slices
    if strcmpi(optS.retainOriginalUIDonExport,'yes') && ...
            isfield(planC{indexS.scan}(i).scanInfo(1),'sopInstanceUID') && ...
            ~isempty(planC{indexS.scan}(i).scanInfo(1).sopInstanceUID)
        for j=1:length(planC{indexS.scan}(i).scanInfo)
            sopInstanceUID = planC{indexS.scan}(i).scanInfo(j).sopInstanceUID;
            sopClassUID = planC{indexS.scan}(i).scanInfo(j).sopClassUID;
            planC{indexS.scan}(i).scanInfo(j).SOP_Class_UID      = sopClassUID;
            planC{indexS.scan}(i).scanInfo(j).SOP_Instance_UID   = sopInstanceUID;
            if isempty(planC{indexS.scan}(i).scanInfo(j).imageOrientationPatient)
                planC{indexS.scan}(i).scanInfo(j).imageOrientationPatient = [1,0,0,0,1,0];
            end
        end
    else
        for j=1:length(planC{indexS.scan}(i).scanInfo)
            planC{indexS.scan}(i).scanInfo(j).SOP_Class_UID      = SOP_Class_UID;
            Frame_Of_Reference_UID = char(javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot));
            planC{indexS.scan}(i).scanInfo(j).SOP_Instance_UID   = Frame_Of_Reference_UID;
            %planC{indexS.scan}(i).scanInfo(j).SOP_Instance_UID   = dicomuid;
            if isempty(planC{indexS.scan}(i).scanInfo(j).imageOrientationPatient)
                planC{indexS.scan}(i).scanInfo(j).imageOrientationPatient = [1,0,0,0,1,0];
            end
        end
    end

end

%% DOSE UIDs
%Iterate over doses
for i = 1:length(planC{indexS.dose})

    %Set the patient id.
    planC{indexS.dose}(i).Patient_ID = Patient_ID;

    %Set patient birth date.
    planC{indexS.dose}(i).Patient_Birth_Date = Patient_Birth_Date;

    % Set the patient name
    planC{indexS.dose}(i).patientName = patientName;

    %Set the study instance UID.
    planC{indexS.dose}(i).Study_Instance_UID = Study_Instance_UID;

    %Set the series number
    seriesNumber = seriesNumber + 1;
    planC{indexS.dose}(i).SeriesNumber = seriesNumber;

    planC{indexS.dose}(i).Study_Date = Study_Date;

    planC{indexS.dose}(i).Study_Time = Study_Time;

    %Generate a series instance UID for each dose;
    %planC{indexS.dose}(i).Series_Instance_UID = dicomuid;
    Series_Instance_UID = javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot);
    planC{indexS.dose}(i).Series_Instance_UID = Series_Instance_UID;

    %Set the frame of reference UID to that of the associated scan, if it
    %exists.
    aS = getDoseAssociatedScan(i, planC);
    if ~isempty(aS)
        planC{indexS.dose}(i).Frame_Of_Reference_UID = planC{indexS.scan}(aS).Frame_Of_Reference_UID;
        planC{indexS.dose}(i).imageOrientationPatient = planC{indexS.scan}(aS).scanInfo(1).imageOrientationPatient;
    else
        %planC{indexS.dose}(i).Frame_Of_Reference_UID = dicomuid;
        Frame_Of_Reference_UID = char(javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot));
        planC{indexS.dose}(i).Frame_Of_Reference_UID = Frame_Of_Reference_UID;
        warning('No associated scan found. Assuming HFS orientation.');
        planC{indexS.dose}(i).imageOrientationPatient = [1 0 0 0 1 0];
    end

    %Generate a SOP ClassUID for each dose.
    SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.481.2';

    planC{indexS.dose}(i).SOP_Class_UID = SOP_Class_UID;
    %planC{indexS.dose}(i).SOP_Instance_UID = dicomuid;
    SOP_Instance_UID = javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot);
    planC{indexS.dose}(i).SOP_Instance_UID = SOP_Instance_UID;

    %Generate a Referenced RT Plan Sequence SOP Class UID for each dose.
    %When plan support is added this must reference actual plans, but for
    %now they are dummy plans.
    planC{indexS.dose}(i).Referenced_RT_Plan_Sequence_SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.481.5';
    %planC{indexS.dose}(i).Referenced_RT_Plan_Sequence_SOP_Instance_UID = dicomuid;
    Referenced_RT_Plan_Sequence_SOP_Instance_UID = javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot);
    planC{indexS.dose}(i).Referenced_RT_Plan_Sequence_SOP_Instance_UID = Referenced_RT_Plan_Sequence_SOP_Instance_UID;

end

%% STRUCTURE UIDs

%Generate a single series instance UID for all structures.
%Structure_Set_Series_UID = dicomuid;
Structure_Set_Series_UID = javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot);

%Generate a single SOP_Class and SOP_Instance ID for all structures;
Structure_Set_SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.481.3';
%Structure_Set_SOP_Instance_UID = dicomuid;
Structure_Set_SOP_Instance_UID = javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot);

%Iterate over structures
seriesNumber = seriesNumber + 1;
for i = 1:length(planC{indexS.structures})

    %Set the patient id.
    planC{indexS.structures}(i).Patient_ID = Patient_ID;

    %Set patient birth date.
    planC{indexS.structures}(i).Patient_Birth_Date = Patient_Birth_Date;

    % Set the patient name
    planC{indexS.structures}(i).patientName = patientName;

    %Set the study instance UID.
    planC{indexS.structures}(i).Study_Instance_UID = Study_Instance_UID;

    %Set the series number
    planC{indexS.structures}(i).SeriesNumber = seriesNumber;

    planC{indexS.structures}(i).Study_Date = Study_Date;

    planC{indexS.structures}(i).Study_Time = Study_Time;

    %Set the series instance UID
    planC{indexS.structures}(i).Series_Instance_UID = Structure_Set_Series_UID;

    %Set the default frame of reference UID to that of the associated scan.
    assocScanNum = getStructureAssociatedScan(i, planC);

    % Set referenced frame of reference UID
    structRefFrameOfReferenceUID = planC{indexS.scan}(assocScanNum).Frame_Of_Reference_UID;

    % Set referenced series UID
    refSeriesInstanceUID = planC{indexS.scan}(assocScanNum).Series_Instance_UID;
    %planC{indexS.scan}(assocScanNum).scanInfo(1).seriesInstanceUID;

    % Set sereferced sopInstanceUIDs
    sopClassUidc = {planC{indexS.scan}(assocScanNum).scanInfo(:).sopClassUID};
    sopInstanceUidc = {planC{indexS.scan}(assocScanNum).scanInfo(:).sopInstanceUID};

%     % Handle special case of assignig reference UID fromanother structure
%     % (e.g. exporting registered images from MIM assistant changes their frameOfreferenceUID)
    if exist('structRefForC','var') && ~isempty(structRefForC)
        ind = ismember(planC{indexS.structures}(i).structureName,structRefForC(:,1));
        if sum(ind) == 1
            structRefFrameOfReferenceUID = structRefForC{ind,2};
            refSeriesInstanceUID = structRefForC{ind,3};
            sopClassUidc = structRefForC{ind,4};
            sopInstanceUidc = structRefForC{ind,5};
        elseif any(ismember('all',structRefForC(:,1)))
                structRefFrameOfReferenceUID = structRefForC{1,2};
                refSeriesInstanceUID = structRefForC{1,3};
                sopClassUidc = structRefForC{1,4};
                sopInstanceUidc = structRefForC{1,5};
        end
    end

    planC{indexS.structures}(i).Frame_Of_Reference_UID = structRefFrameOfReferenceUID;
    planC{indexS.structures}(i).Referenced_Series_Instance_UID = refSeriesInstanceUID;

    %Set the SOP UIDs.
    planC{indexS.structures}(i).SOP_Class_UID    = Structure_Set_SOP_Class_UID;
    planC{indexS.structures}(i).SOP_Instance_UID = Structure_Set_SOP_Instance_UID;

    % set the referenced SOP UID for each slice
    for slc = 1:length(planC{indexS.structures}(i).contour)
        planC{indexS.structures}(i).contour(slc).SOP_Class_UID = sopClassUidc{slc};
        planC{indexS.structures}(i).contour(slc).SOP_Instance_UID = sopInstanceUidc{slc};
    end

    aS = getStructureAssociatedScan(i, planC);
    if ~isempty(aS)
        planC{indexS.structures}(i).imageOrientationPatient = planC{indexS.scan}(aS).scanInfo(1).imageOrientationPatient;
    else
        warning('No associated scan found. Assuming HFS orientation.');
        planC{indexS.structures}(i).imageOrientationPatient = [1 0 0 0 1 0];
    end

end

%% DVH UIDs

%Set the associated structure set UIDs for all DVHs to the same values.
Referenced_Structure_Set_SOP_Class_UID    = Structure_Set_SOP_Class_UID;
Referenced_Structure_Set_SOP_Instance_UID = Structure_Set_SOP_Instance_UID;

%Iterate over DVHs
seriesNumber = seriesNumber + 1;
for i = 1:length(planC{indexS.DVH})
    planC{indexS.DVH}(i).Referenced_Structure_Set_SOP_Class_UID     = Referenced_Structure_Set_SOP_Class_UID;
    planC{indexS.DVH}(i).Referenced_Structure_Set_SOP_Instance_UID  = Referenced_Structure_Set_SOP_Instance_UID;
    planC{indexS.DVH}(i).SeriesNumber = seriesNumber;
end

%% GSPS UIDs
%Iterate over GSPS
for scanNum = 1:length(planC{indexS.scan})
    originalSliceSOPInstanceUIDc = {planC{indexS.scan}(scanNum).scanInfo.sopInstanceUID};
    seriesNumber = seriesNumber + 1;
    for i = 1:length(planC{indexS.GSPS})

        % scanNum associated with this GSPS (expand to handle multiple scans)
        %scanNum = 1;

        %Set the patient id
        planC{indexS.GSPS}(i).Patient_ID = Patient_ID;

        %Set the patient birth date
        planC{indexS.GSPS}(i).Patient_Birth_Date = Patient_Birth_Date;

        % Set the patient name
        planC{indexS.GSPS}(i).patientName = patientName;

        %Set the study instance UID.
        planC{indexS.GSPS}(i).Study_Instance_UID = Study_Instance_UID;

        planC{indexS.GSPS}(i).SeriesNumber = seriesNumber;

        planC{indexS.GSPS}(i).Study_Date = Study_Date;

        planC{indexS.GSPS}(i).Study_Time = Study_Time;

        %Generate a series instance UID for each dose;
        %planC{indexS.GSPS}(i).Series_Instance_UID = dicomuid;
        %Series_Instance_UID = javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot);
        %planC{indexS.GSPS}(i).Series_Instance_UID = Series_Instance_UID;

        %Set the frame of reference UID to that of the associated scan, if it
        %exists.
        planC{indexS.GSPS}(i).Frame_Of_Reference_UID = planC{indexS.scan}(1).Frame_Of_Reference_UID;

        %Generate a SOP ClassUID for each GSPS.
        SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.11.1';

        planC{indexS.GSPS}(i).SOP_Class_UID = SOP_Class_UID;
        %planC{indexS.GSPS}(i).SOP_Instance_UID = dicomuid;
        SOP_Instance_UID = char(javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot));
        planC{indexS.GSPS}(i).SOP_Instance_UID = SOP_Instance_UID;

        if strcmpi(optS.retainOriginalUIDonExport,'no')
            sopInstanceUID = planC{indexS.GSPS}(i).SOPInstanceUID;
            sliceNum = strncmp(sopInstanceUID,originalSliceSOPInstanceUIDc,length(sopInstanceUID));
            referenced_SOP_instance_uid = ...
                planC{indexS.scan}(scanNum).scanInfo(sliceNum).SOP_Instance_UID;
            referenced_SOP_class_uid = ...
                planC{indexS.scan}(scanNum).scanInfo(sliceNum).SOP_Class_UID;
            Series_Instance_UID = char(javaMethod('createUID','org.dcm4che3.util.UIDUtils',orgRoot));
            planC{indexS.GSPS}(i).Series_Instance_UID = Series_Instance_UID;
        else
            referenced_SOP_instance_uid = planC{indexS.GSPS}(i).SOPInstanceUID;
            referenced_SOP_class_uid = ...
                planC{indexS.scan}(scanNum).scanInfo(1).SOP_Class_UID;
            series_Instance_UID = planC{indexS.scan}(scanNum).scanInfo(1).seriesInstanceUID;
            planC{indexS.GSPS}(i).Series_Instance_UID = series_Instance_UID;
        end
        planC{indexS.GSPS}(i).referenced_SOP_instance_uid = referenced_SOP_instance_uid;
        planC{indexS.GSPS}(i).referenced_SOP_class_uid = referenced_SOP_class_uid;

        %Generate a Referenced Study Sequence SOP Class UID for each dose.
        %planC{indexS.GSPS}(i).Referenced_Study_Sequence_SOP_Class_UID = '1.2.840.10008.5.1.4.1.1.481.5';
        %planC{indexS.GSPS}(i).Referenced_Study_Sequence_SOP_Instance_UID = dicomuid;

        %Generate a Referenced Series Sequence SOP Class UID for each dose.

    end
end


function bz_RunAnimalMetadata(basepath)

%% Initial variable parsing
if ~exist('basepath','var')
    basepath = cd;
elseif isempty(basepath)
    basepath = cd;
end
basename = bz_BasenameFromBasepath(basepath);

%%
bz_EditAnimalMetadata(basepath)

%%
notesname = [basename,'_AnimalMetadataText'];
notesfullpath = fullfile(basepath,notesname);
prompt = 'Push any key in this window when done editing the NoteText file ';
str = input(prompt,'s');
run(notesfullpath);


function stats = applyStatsfun(problem, x, storedb, options, stats)
% Apply the statsfun function to a stats structure (for solvers).
%
% function stats = applyStatsfun(problem, x, storedb, options, stats)
%
% Applies the options.statsfun user supplied function to the stats
% structure, if it was provided, with the appropriate inputs, and returns
% the (possibly) modified stats structure.
%
% See also:

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, April 3, 2013.
% Contributors:
% Change log:

	if isfield(options, 'statsfun')

		is_octave = exist('OCTAVE_VERSION', 'builtin');
		if ~is_octave
			narg = nargin(options.statsfun);
		else
			narg = 4;
		end

        switch narg
            case 3
                stats = options.statsfun(problem, x, stats);
            case 4
                store = getStore(problem, x, storedb);
                stats = options.statsfun(problem, x, stats, store);
            otherwise
                warning('manopt:statsfun', ...
                        'statsfun unused: wrong number of inputs');
        end
	end

end


 function [opt, extra] = vararg_pair(opt, varargs, varargin)
%function [opt, extra] = vararg_pair(opt, varargs, [options])
%|
%| Process name / value pairs, replacing the "default" field values
%| of the opt structure with the user-specified values.
%| This allows flexible argument order and "named arguments" somewhat like IDL.
%| This newer version allows option names to differ from the field names.
%| If two output arguments are given, then any "extra" name value pairs
%| that are not associated with the fields in structure opt will be
%| returned as name/value pairs in the "extra" output.
%|
%| in
%|	opt	struct	opt.name1 opt.name2 etc., containing default values
%|	varargs	{'name1', 'value1', ... } as a cell array
%|
%| options
%|	'subs'	Nx2 cell	{'arg_name1', 'struct_name1'; ...}
%|		short argument name followed by longer structure name
%|	'allow_new'	0|1	allow new structure elements? (default: 0)
%|
%| out
%|	opt		opt.(name) = value
%|	extra		{'name_i', 'value_i', ...} all "unused" pairs
%|
%| Copyright 2005-06-18, Jeff Fessler, University of Michigan
%| Modified 2016-04-06, Gopal Nataraj, University of Michigan

if nargin < 1, ir_usage, end
if nargin == 1 && streq(opt, 'test'), vararg_pair_test, clear, return, end

if isempty(varargs) && numel(varargin) == 0
	extra = {};
	return
end

base = [caller_name ' arg: ']; % for printing

local.subs = {};
local.allow_new = 0;
local = vararg_pair_do(local, varargin, {}, 0, 0);

allow_extra = nargout > 1;
[opt, extra] = vararg_pair_do(opt, varargs, local.subs, local.allow_new, ...
	allow_extra, base);

% Structure elements of opt with value "NaN" are checked as being required.
% NO, this just adds overhead. Removed 2007-2-9.
% vararg_pair_check(opt);

% vararg_pair_do()
function [opt, extra] = ...
	vararg_pair_do(opt, varargs, subs, allow_new, allow_extra, base)

if allow_extra && allow_new
	error 'only one of "new" or "extra" is sensible for vararg_pair'
end

npair = floor(length(varargs) / 2);
if 2*npair ~= length(varargs), fail('need names and values in pairs'), end
args = varargs(1:2:end);
vals = varargs(2:2:end);

if ~isempty(subs)
	if size(subs,2) ~= 2, fail('subs must be Nx2'), end
%	subs1 = strvcat(subs(:,1));
	subs1 = char(subs(:,1)); % strmatch doc says fastest for char array!
	subs2 = subs(:,2);
	% todo: apply 'subs' all at once here?
end

extra = {};
for ii=1:npair
	arg = args{ii};
	val = vals{ii};

	if ~ischar(arg)
		fail('unknown option of class %s', class(arg))
	end

	if ~isempty(subs)
		arg = vararg_pair_sub(arg, subs1, subs2);
	end

	[,opt ex] = vararg_pair_put(opt, arg, val, allow_new, allow_extra);
	if allow_extra && ~isempty(ex)
		extra = [extra(:); ex(:)]'; % faster than {extra{:}, ex{:}};
	end

	if isfield(opt, 'chat') && opt.chat && isempty(ex)
		show_pair(arg, val, base)
	end
end

% vararg_pair_sub()
% substitute option name for field name, if a Nx2 cell array is provided.
%
function arg = vararg_pair_sub(arg, subs1, subs2)
ii = strmatch(arg, subs1, 'exact');
if isempty(ii)
	return
elseif length(ii) == 1
	arg = subs2{ii};
else
	printm([subs1(ii) ' ' subs2{ii}])
	error 'double match? only one substitution allowed'
end

% vararg_pair_put()
% opt.(arg) = value
% with extra effort to handle opt.(arg1.arg2) = value
% because matlab does not handle that case by itself, except via subsagn
%
function [opt, extra] = vararg_pair_put(opt, arg, value, allow_new, allow_extra)

extra = {};
if isfield(opt, arg) % simple case of name / value pair
	opt.(arg) = value;
	return
end

idot = strfind(arg, '.');

% old: jf, 2005
%{
if isempty(idot)
	if allow_new
		opt.(arg) = value;
		return
	elseif allow_extra
		extra = {arg, value};
		return
	else
		fail('unknown option name "%s"', arg)
	end
end

% tricky case of a.b.c
if length(idot) > 1, error 'a.b.c.d not done', end

arg1 = arg([1:(idot-1)]);
arg2 = arg([(idot+1):end]);
if ~isfield(opt, arg1)
	fail('unknown option1 name "%s"', arg1)
end
if ~isfield(opt.(arg1), arg2)
	fail('unknown option2 name %s', arg2)
end
s = struct('type', {'.', '.'}, 'subs', ...
	{ arg([1:(idot-1)]), arg([(idot+1):end]) });
try
	opt = subsasgn(opt, s, value);
catch
	printf(lasterr)
	fail('subsasgn? unknown option name %s', arg)
end
%}

% new: gn, 2016
switch length(idot)
	case 0
		if allow_new
			opt.(arg) = value;
			return
		elseif allow_extra
			extra = {arg, value};
			return
		else
			fail('unknown option name "%s"', arg)
		end

	case 1
		arg1 = arg([1:(idot-1)]);
		arg2 = arg([(idot+1):end]);
		if ~isfield(opt, arg1)
			fail('unknown option1 name "%s"', arg1)
		end
		if ~isfield(opt.(arg1), arg2)
			fail('unknown option2 name "%s"', arg2)
		end
		s = struct('type', {'.', '.'}, 'subs', {arg1, arg2});
		try
			opt = subsasgn(opt, s, value);
		catch
			 printm(lasterr)
			 fail('subsasgn? unknown option name %s', arg)
		end

	case 2
		arg1 = arg([1:(idot(1)-1)]);
		arg2 = arg([(idot(1)+1):(idot(2)-1)]);
		arg3 = arg([(idot(2)+1):end]);
		if ~isfield(opt, arg1)
			fail('unknown option1 name "%s"', arg1)
		end
		if ~isfield(opt.(arg1), arg2)
			fail('unknown option2 name "%s"', arg2)
		end
		if ~isfield(opt.(arg1).(arg2), arg3)
			fail('unknown option3 name "%s"', arg3)
		end
		s = struct('type', {'.', '.', '.'},...
			'subs', {arg1, arg2, arg3});
		try
			opt = subsasgn(opt, s, value);
		catch
			printf(lasterr)
			fail('subsasgn? unknown option name %s', arg)
		end

	otherwise
		error('a.b.c.d.e not done');
end

% vararg_pair_check()
% check that the user supplied the required arguments,
% which are signified by 'nan' values.
%
function vararg_pair_check(opt)
names = fieldnames(opt);
ok = 1;
for ii=1:length(names)
	t = opt.(names{ii});
	if isnumeric(t) && any(isnan(t(:)))
		printf('Required option "%s" missing', names{ii});
		ok = 0;
	end
end
if ~ok, fail 'missing required "option(s)"', end

% show_pair()
function show_pair(name, value, base)

pri = @(varargin) printf([base varargin{1}], varargin{2:end});

if isnumeric(value) || islogical(value)
	if max(size(value)) == 1	% only print scalars
		if ~streq(name, 'chat')
			pri('%s = %g', name, value)
		end
	else	% otherwise print sum (e.g., for mask)
		pri('sum(%s) = %g', name, sum(value(:)))
	end

elseif ischar(value)
	pri('%s = %s', name, value)

elseif isa(value, 'cell')
	pri('%s {cell}', name)

elseif isa(value, 'function_handle')
	pri('%s = @%s', name, func2str(value))

elseif isstruct(value)	% show first structure element, if string
	fields = fieldnames(value);
%	name = getfield(arg, fields{1});
	name1 = value.(fields{1});
	if ischar(name1)
		pri('%s.%s = %s', name, fields{1}, name1)
	end

else
	warn('display type "%s" not implemented', class(value))
end

% test routine
function vararg_pair_test
args = {'a', 1, 'b', 2, 'c.d', 3, 'g', 9, 'l', 5, 'll', 6};%, 'c.f', 4};
opt.a = 0;
opt.b = 0;
opt.f = 0;
opt.long1 = 0;
opt.long2 = 0;
%opt.g = nan;
opt.c.d = 0;
opt.c.e = 0;
sub = {'g', 'f'; 'l', 'long1'; 'll', 'long2'};
opt = vararg_pair(opt, args, 'subs', sub);
jf_equal(opt.a, 1)
jf_equal(opt.b, 2)
jf_equal(opt.c.d, 3)
jf_equal(opt.c.e, 0)
jf_equal(opt.f, 9)
jf_equal(opt.long1, 5)
jf_equal(opt.long2, 6)

opt = vararg_pair(opt, {'z', 'new'}, 'allow_new', 1);
if ~isequal(opt.z, 'new'), fail('bug'), end
try % typo in name should yield error
	opt = vararg_pair(opt, {'verify_bad_test!', 'new'}, 'allow_new', 0)
catch
	printm 'test passed'
	return
end
fail 'should not get here'


function varargout = ihashsum(varargin)
% VL_IHASHSUM
[varargout{1:nargout}] = vl_ihashsum(varargin{:});


function test (varargin)
% TODO: in stack information, replace pointers to temporary files with
%       pointes to test section in the mfile being tested.
% TODO: add test case for get_stack_trace
% TODO: add test case for functions other than m-files that have
%       an accompanying homonymous m-file containing the test blocks.
% TODO: support subfunctions. (Nested functions will never be supported
%!      because they cannot stand alone.)
% TODO: is seems that a test block, at the end of a file, without a blank
%       line after it, is not supported.

    %%%%%%%%%%
    switch nargin
    case 0
        error('test:notEnoughInputs', ...
        'Not enough input arguments.');
    case 1
        mfunc = varargin{1};
        param = '';

        mfile = which(mfunc);
        if isempty(mfile)
            error('test:UndefinedFunction', ...
            'Function %s not found in path.', mfunc);
        end
        [mdir, mfunc, ext] = fileparts(mfile);
        if ~strcmp(ext, '.m')
            mfile = fullfile(mdir, [mfunc '.m']);
            if ~exist(mfile, 'file')
                error('test:FileNotFound',...
                'File not found: %s', mfile);
            end
        end

        %[tdir, tfunc] = fileparts(tempname);
        %tfile = fullfile(tdir, [tfunc '.m']);
        % under Vista, it's significantly faster to addpath
        % an empty directory rather than the standard temporary dir.
        tdir = tempname;
        tfunc = 'testit';
        tfile = fullfile(tdir, [tfunc '.m']);

        pdir = tdir;
        create_pdir = true;

        test_type = 'func';
    case 2
        mfunc = varargin{1};
        param = varargin{2};

        try
            eval(sprintf('%s;', param));
        catch
            param = sprintf('%s(1)', param);
        end
        mfunc2 = sprintf('%s(%s)', mfunc, param);
        mfile = which(mfunc2);
        if isempty(mfile)
            error('test:UndefinedFunction',...
            'Function %s not found in path.', mfunc2);
        end
        [mdir, mfunc, ext] = fileparts(mfile);
        if ~strcmp(ext, '.m')
            mfile = fullfile(mdir, [mfunc '.m']);
            if ~exist(mfile, 'file')
                error('test:FileNotFound',...
                'File not found: %s', mfile);
            end
        end

        %[tdir, tfunc] = fileparts(tempname);
        %tfile = fullfile(tdir, [tfunc '.m']);
        % under Vista, it's significantly faster to addpath
        % an empty directory rather than the standard temporary dir.
        tdir = tempname;
        tfunc = 'testit';
        tfile = fullfile(tdir, [tfunc '.m']);

        pdir = tdir;
        create_pdir = true;

        test_type = 'method';
    case 3
        mfunc = varargin{1};
        param = char(varargin{2});  % so that printf works when param = []
        mdir  = varargin{3};

        if ~isempty(param) && strcmp(mdir, 'private')
            mdir = [fileparts(which(param)) '\private'];
        end
        if ~(   strend([filesep 'private' filesep], mdir) ...
             || strend([filesep 'private']        , mdir) )
            error('test:DirEndInPrivate',...
            ['Third parameter %s should end in '...
            '"%cprivate".'], mdir, filesep);
        end

        mfile = fullfile(mdir, [mfunc '.m']);
        if ~exist(mfile, 'file')
            error('test:FileNotFound',...
            'File not found: %s', mfile);
        end

        tdir = dirup(mdir);
        create_pdir = false;
        while true
            [temp, tfunc] = fileparts(tempname); %#ok<ASGLU>
            tfile = fullfile(tdir, [tfunc '.m']);
            if ~exist(tfile, 'file'),  break;  end
        end

        if isempty(param)
            pdir = tdir;
            test_type = 'priv_func';
        else
            if ~strend([filesep '@' param], tdir)
                error('test:DirEndInClass',...
                ['Third parameter %s should end in '...
                '"%c@%s%cprivate".'], mdir, filesep, param, filesep);
            end
            pdir = dirup(tdir);
            test_type = 'priv_method';
        end
    otherwise
        error('test:tooManyInputs',...
        'Too many input parameters.');
    end
    %mdir, tdir, pdir  % DEBUG

    %%%%%%%%%%
    %mfile  % DEBUG
    %type(mfile)  % DEBUG
    [mfid, msg] = fopen (mfile, 'rt');
    if (mfid == -1)
        error('test:FileOpening', msg);
    end

    % extract blocks (test, error, shared) in m-file
    block_start = [];
    is_in_block = false;
    i = 1;  % # of %! lines
    j = 1;  % # of test or error blocks
    mline = fgets(mfid);
    while ~isequal(mline, -1)  % while not end of file
        if (   strncmp(mline, '%!test', 6) ...
            || strncmp(mline, '%!error', 7) ...
            || strncmp(mline, '%!shared', 8) )
            % start block
            is_in_block = true;
            block_start(j) = i;
            if strncmp(mline, '%!test', 6)
                block_type{j} = 'test';
            elseif strncmp(mline, '%!error', 7)
                block_type{j} = 'error';
            elseif strncmp(mline, '%!shared', 8)
                block_type{j} = 'shared';
            end
        elseif is_in_block && strncmp(mline, '%!', 2)
            % read block contents
            tline{i} = mline(4:end);
            i = i + 1;
        elseif is_in_block
            % end block
            block_end(j) = i-1;
            j = j + 1;
            is_in_block = false;
        end
        mline = fgets(mfid);
    end
    if is_in_block
        % end latest block
        block_end(j) = i-1;
    end
    fclose(mfid);

    if isempty(block_start) || all(strcmp(block_type, 'shared'))
        error('test:noBlock',...
        'Found no test or error section in file %s.', mfile);
    end
    myassert (length(block_start), length(block_end));
    myassert (length(block_start), length(block_type));

    %%%%%%%%%%

    %% create test file
    old_path = path;
    if create_pdir,  mkdir(pdir);  end
    addpath(pdir);
    %addpath(pdir, '-frozen');

    %% write and run each block
    for j=1:length(block_start)
        if strcmp(block_type{j}, 'shared'),  continue;  end

        [tfid, msg] = fopen(tfile, 'wt');
            if (tfid == -1),  error('test:FileOpening', msg);  end

        if strcmp(test_type, 'priv_method')
            fprintf(tfid, 'function %s (obj)\n', tfunc);
        else
            fprintf(tfid, 'function %s\n', tfunc);
        end

        found_mfunc_call = false;

        %% Write optional, previous, shared block.
        k = find (strcmp({block_type{1:j}}, 'shared'), 1, 'last');
        if ~isempty(k)
            myassert (strcmp(block_type{k}, 'shared'));
            fprintf(tfid, '%% shared\n');
            for i=block_start(k):block_end(k)
                fprintf(tfid, '%s', tline{i});
                found_mfunc_call = found_mfunc_call ...
                                   || is_function_called (tline{i}, mfunc);
            end
            fprintf(tfid, '\n');
        end

        %% Is it an error block?
        if strcmp(block_type{j}, 'error')
            fprintf(tfid, 'try\n');
            % In an error block, error is the expected behaviour.
            % We do we following:
            %     try,
            %         % INSERT USER CODE HERE
            %     catch,
            %         return;
            %     end
            %     error('test:error:fail', '');
            % If the user code generates an error, it is catched and
            % the test runs OK. If the user code does _not_ generate
            % an error(contrary to what was expected), then the test fails.
        end

        for i=block_start(j):block_end(j)
            fprintf(tfid, '%s', tline{i});
            found_mfunc_call = found_mfunc_call ...
                               || is_function_called (tline{i}, mfunc);
        end

        if (~found_mfunc_call)
            warning('test:noFuncCall', ...
                'Found no call to function %s itself in its %s block #%d.',...
                mfunc, block_type{j}, j);
        end

        if strcmp(block_type{j}, 'error')
            fprintf(tfid, 'catch\n');
            fprintf(tfid, '    return;\n');
            fprintf(tfid, 'end\n');
            fprintf(tfid, 'clear s;\n');
            fprintf(tfid, 's.identifier=''test:error:fail'';\n');
            fprintf(tfid, 's.message=''No error found in error section.'';\n');
            fprintf(tfid, 'error(s);\n');
        end

        fprintf(tfid, 'end\n');
        fclose(tfid);
        %type(tfile); % DEBUG

        % reload function definition:
        clear(tfile);
        if strcmp(test_type, 'priv_method')
            % we have added a new class methods; reload the class:
            clear(which(param));
        else
            clear(which(tfunc));
        end

        %% run test file
        try
            if strcmp(test_type, 'priv_method')
                eval(sprintf('%s(%s);', tfunc, param));
            else
                %which(tfunc),  type(which(tfunc))  % DEBUG
                feval(tfunc);
                %run([tdir filesep tfunc]);
            end
        catch %#ok<CTCH>
            s = lasterror; %#ok<LERR>
            idx = strcmp({s.stack.name}, mfunc);
            if strcmp(test_type, 'priv_func') && any(idx)
                s.stack(idx).file = mfile;  % it needs full path
            end
            type(which(tfunc));
            s.message = [s.message, get_stack_trace(s)];
            %if ~strcmp(s.identifier, 'MATLAB:UndefinedFunction')
                delete(tfile);
            %end
            path(old_path);
            if create_pdir,  rmdir(pdir);  end
            rethrow(s);
        end
    end

    %% remove test file
    if exist(tfile, 'file'),  delete(tfile);  end
    path(old_path);
    if create_pdir,  rmdir(pdir);  end
end

%!#test
%! % test()
%! myassert (false)

%!test
%! % test
%! myassert(true);
%! lasterr('', 'test:testing');

%!test
%! % test
%! s = lasterror;
%! myassert(s.identifier, 'test:testing');
%! lasterr('', '');

%!error
%! % test
%! myassert(false);

%!shared
%! var = pi;

%!test
%! % test
%! myassert (var == pi);
%! var = 2*pi;

%!test
%! % test
%! myassert (var == pi);

%!shared
%! var = 3*pi;

%!test
%! % test
%! myassert (var == 3*pi);

%!shared  % to clear previous %!shared

%!error
%! test;

%! % test
%! s = lasterror;
%! myassert(s.identifier, 'test:notEnoughInputs');

%!error
%! test(tempname);

%! % test
%! s = lasterror;
%! myassert(s.identifier, 'test:UndefinedFunction');

%!error
%! test(1, 2, 3, 4);

%! % test
%! s = lasterror;
%! myassert(s.identifier, 'test:tooManyInputs');

%!test
%! % It's not an error to have a single, empty test section.
%! func = tempname;
%! file = [func '.m'];
%!
%! fid = fopen(file, 'wt');
%! fprintf(fid, '%%!test\n');
%! fprintf(fid, '%%! %% %s\n', func);
%! fclose(fid);
%!
%! old_path = path;
%! addpath(fileparts(file));
%!
%! lasterr ('', '');
%! try,  test(func);  end
%! s = lasterror;
%! %type (file);  % DEBUG
%! delete (file);
%! path(old_path);
%! myassert (isempty(s.identifier));

%!test
%! % Issue an error if there is shared section but not test or error sections.
%! func = tempname;
%! file = [func '.m'];
%!
%! fid = fopen(file, 'wt');
%! fprintf(fid, '%%!shared\n');
%! fprintf(fid, '%%! \n');
%! fclose(fid);
%!
%! old_path = path;
%! addpath(fileparts(file));
%!
%! lasterr ('', '');
%! try,  test(func);  end
%! s = lasterror;
%! %type (file);  % DEBUG
%! delete (file);
%! path(old_path);
%! myassert (s.identifier, 'test:noBlock');

%!shared
%! % Here we generate the following directory-tree:
%! % dir/
%! %     @aclass/
%! %         aclass.m  % class constructor
%! %         afunc.m
%! % Then test() creates:
%! % dir/
%! %     atest.m
%!
%! class_code = {...
%!     'function answer = aclass (value)'
%!     '    if (nargin < 1),  value = [];  end'
%!     '    answer.value = value;'
%!     '    answer = class(answer, ''aclass'');'
%!     'end'
%! };
%! func_code = {...
%!     'function answer = afunc (obj)'
%!     '    answer = obj.value;'
%!     'end'
%!     ''
%!     '%!shared'
%!     '%! value = pi;'
%!     '%! obj = aclass(value);'
%!     ''
%!     '%!error'
%!     '%! % afunc'
%!     '%! myassert(false);'
%!     ''
%!     '%!test'
%!     '%! myassert(afunc(obj), value);'
%! };
%! %class_code, func_code  % DEBUG
%!
%! dir = tempname;
%! class_dir = fullfile(dir, '@aclass');
%! temp = mkdir(class_dir);
%!     myassert(temp ~= 0);
%!
%! class_file = fullfile(class_dir, 'aclass.m');
%! [class_fid, msg] = fopen (class_file, 'wt');
%!     if (class_fid == -1),  error(msg);  end
%! fprintf(class_fid, '%s\n', class_code{:});
%! temp = fclose(class_fid);
%!     myassert (temp ~= -1);
%!
%! func_dir = class_dir;
%! func_file = fullfile(func_dir, 'afunc.m');
%! func_fid = fopen (func_file, 'wt');
%!     myassert (func_fid ~= -1);
%! fprintf(func_fid, '%s\n', func_code{:});
%! temp = fclose(func_fid);
%!     myassert (temp ~= -1);
%!
%! %type(class_file);  type(func_file);  % DEBUG
%! %pause  % DEBUG
%!
%! old_path = path;
%! addpath (dir);  % NOT addpath (class_dir);

%!test
%! % Test a class method:
%! % test('afunc', 'aclass');
%!
%! try,
%!     test('afunc', 'aclass');
%! catch,
%!     s = lasterror;
%!     if ~strcmp(s.identifier, 'MATLAB:UndefinedFunction')
%!         path (old_path);
%!         [status, msg, msgid] = rmdir(dir, 's');
%!             if (status == 0),  error(msgid, msg);  end
%!     end
%!     rethrow (s);
%! end
%!
%! path (old_path);
%! [status, msg, msgid] = rmdir(dir, 's');
%!     if (status == 0),  error(msgid, msg);  end

%!test
%! % Test a class private method:
%! % test('afunc', 'aclass', 'adir');
%! %
%! % Here we generate the following directory-tree:
%! % dir/
%! %     @aclass/
%! %         aclass.m  % class constructor
%! %         private/
%! %             afunc.m
%! % Then test() creates:
%! % dir/
%! %     @aclass/
%! %         atest.m
%!
%! func_file_old = func_file;
%! func_dir = fullfile(class_dir, 'private');
%! temp = mkdir(func_dir);
%!     myassert(temp ~= 0);
%! func_file = fullfile(func_dir, 'afunc.m');
%! [status, msg, msgid] = movefile (func_file_old, func_file);
%!     if (status ~= 1),  error(msgid, msg);  end
%!
%! try,
%!     %keyboard  % DEBUG
%!     test('afunc', 'aclass', func_dir);
%! catch,
%!     s = lasterror;
%!     if ~strcmp(s.identifier, 'MATLAB:UndefinedFunction')
%!         path (old_path);
%!         [status, msg, msgid] = rmdir(dir, 's');
%!             if (status == 0),  error(msgid, msg);  end
%!     end
%!     rethrow (s);
%! end
%!
%! path (old_path);
%! [status, msg, msgid] = rmdir(dir, 's');
%!     if (status == 0),  error(msgid, msg);  end

%!test
%! % Test a class private method, letting test() guess adir:
%! % test('afunc', 'aclass', 'private');
%! %
%! % Here we generate the following directory-tree:
%! % dir/
%! %     @aclass/
%! %         aclass.m  % class constructor
%! %         private/
%! %             afunc.m
%! % Then test() creates:
%! % dir/
%! %     @aclass/
%! %         atest.m
%!
%! func_file_old = func_file;
%! func_dir = fullfile(class_dir, 'private');
%! temp = mkdir(func_dir);
%!     myassert(temp ~= 0);
%! func_file = fullfile(func_dir, 'afunc.m');
%! [status, msg, msgid] = movefile (func_file_old, func_file);
%!     if (status ~= 1),  error(msgid, msg);  end
%!
%! try,
%!     %keyboard  % DEBUG
%!     test('afunc', 'aclass', 'private');
%! catch,
%!     s = lasterror;
%!     if ~strcmp(s.identifier, 'MATLAB:UndefinedFunction')
%!         path (old_path);
%!         [status, msg, msgid] = rmdir(dir, 's');
%!             if (status == 0),  error(msgid, msg);  end
%!     end
%!     rethrow (s);
%! end
%!
%! path (old_path);
%! [status, msg, msgid] = rmdir(dir, 's');
%!     if (status == 0),  error(msgid, msg);  end

%!shared  % to clear previous %!shared

%!test
%! % Test a private function:
%! % test('afunc', [], 'adir');
%! %
%! % Here we generate the following directory-tree:
%! % dir/
%! %     private/
%! %         afunc.m
%! % Then test() creates:
%! % dir/
%! %     atest.m
%!
%! func_code = {...
%!     'function out = afunc (in)'
%!     '    if isequal(in, 0),  error(''afunc:any'', '''');  end'
%!     '    out = 1/in;'
%!     'end'
%!     ''
%!     '%!error'
%!     '%! afunc(0)';
%!     ''
%!     '%!test'
%!     '%! myassert(afunc(pi), 1/pi);'
%! };
%! %func_code  % DEBUG
%!
%! dir = tempname;
%! temp = mkdir(dir);
%!     myassert(temp ~= 0);
%! temp = mkdir(dir, 'private');
%!     myassert(temp ~= 0);
%!
%! func_dir = fullfile(dir, 'private');
%! func_file = fullfile(func_dir, 'afunc.m');
%! [func_fid, msg] = fopen (func_file, 'wt');
%!     if (func_fid == -1),  error(msg);  end
%! fprintf(func_fid, '%s\n', func_code{:});
%! temp = fclose(func_fid);
%!     myassert (temp ~= -1);
%! %type(func_file);  % DEBUG
%!
%! old_path = path;
%! addpath (dir);  % NOT addpath (func_dir);
%! test('afunc', [], func_dir);
%! path (old_path);
%!
%! [status, msg, msgid] = rmdir(dir, 's');
%!     if (status == 0),  error(msgid, msg);  end

%!#test
%! % Check if test files are being deleted,
%! % even in case of error.
%!
%! code = {...
%!     'function answer = func'
%!     '    persistent temp'
%!     '    if isempty(temp)'
%!     '        temp = which(get_caller_name);'
%!     '    end'
%!     '    answer = temp;'
%!     'end'
%! };
%! test1 = {...
%!     '%!test'
%!     '%! func;'
%!     '%! myassert(true);'
%! };
%! test2 = {...
%!     '%!test'
%!     '%! func;'
%!     '%! myassert(false);  % THROW ERROR'
%! };
%!
%! dir = tempname;
%! mkdir(dir);
%! file = fullfile(dir, 'func.m');
%!
%! old_path = path;
%! addpath(dir);
%!
%! for i=1:2
%!     switch i
%!     case 1
%!         testi = test1;
%!     case 2
%!         testi = test2;
%!     end
%!
%!     fid = fopen(file, 'wt');
%!     fprintf(fid, '%s\n', code{:});
%!     fprintf(fid, '%s\n', testi{:});
%!     fclose(fid);
%!
%!     clear('func');
%!     try,  test('func');  end
%!
%!     tfile = func;
%!     try,
%!         switch i
%!         case 1
%!             myassert (~exist(tfile, 'file'));
%!         case 2
%!             myassert (~exist(tfile, 'file'));
%!         end
%!     catch,
%!         s = lasterror;
%!         path(old_path);
%!         rmdir(dir, 's');
%!         rethrow (s);
%!     end
%! end
%! path(old_path);
%! rmdir(dir, 's');

%!test
%! % test ()
%! lastwarn('', '');  % clear last warning
%! warning('off', 'test:noFuncCall');

%!test
%! % generate warning

%!test
%! % test ()
%! [msg, msgid] = lastwarn;
%! myassert (msgid, 'test:noFuncCall');  % check warning
%! warning('on', 'test:noFuncCall');

%!test
%! % test ()
%! lastwarn('', '');  % clear last warning

%!shared
%! % test ()  % avoid test()'s warning

%!test
%! [msg, msgid] = lastwarn;
%! myassert (msgid, '');  % check warning is still clear


function varargout = lvd_editThrottleModifierProfileElementGUI(varargin)
% LVD_EDITTHROTTLEMODIFIERPROFILEELEMENTGUI MATLAB code for lvd_editThrottleModifierProfileElementGUI.fig
%      LVD_EDITTHROTTLEMODIFIERPROFILEELEMENTGUI, by itself, creates a new LVD_EDITTHROTTLEMODIFIERPROFILEELEMENTGUI or raises the existing
%      singleton*.
%
%      H = LVD_EDITTHROTTLEMODIFIERPROFILEELEMENTGUI returns the handle to a new LVD_EDITTHROTTLEMODIFIERPROFILEELEMENTGUI or the handle to
%      the existing singleton*.
%
%      LVD_EDITTHROTTLEMODIFIERPROFILEELEMENTGUI('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in LVD_EDITTHROTTLEMODIFIERPROFILEELEMENTGUI.M with the given input arguments.
%
%      LVD_EDITTHROTTLEMODIFIERPROFILEELEMENTGUI('Property','Value',...) creates a new LVD_EDITTHROTTLEMODIFIERPROFILEELEMENTGUI or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before lvd_editThrottleModifierProfileElementGUI_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to lvd_editThrottleModifierProfileElementGUI_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help lvd_editThrottleModifierProfileElementGUI

% Last Modified by GUIDE v2.5 08-Jan-2019 21:37:20

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @lvd_editThrottleModifierProfileElementGUI_OpeningFcn, ...
                   'gui_OutputFcn',  @lvd_editThrottleModifierProfileElementGUI_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before lvd_editThrottleModifierProfileElementGUI is made visible.
function lvd_editThrottleModifierProfileElementGUI_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to lvd_editThrottleModifierProfileElementGUI (see VARARGIN)

    % Choose default command line output for lvd_editThrottleModifierProfileElementGUI
    handles.output = hObject;

    centerUIFigure(hObject);

    elem = varargin{1};
    setappdata(hObject,'elem',elem);

    allowFuPctEdit = varargin{2};
    setappdata(hObject,'allowFuPctEdit',allowFuPctEdit);

    curve = varargin{3};
    setappdata(hObject,'curve',curve);

    % Update handles structure
    guidata(hObject, handles);

    populateUI(elem, allowFuPctEdit, curve, handles);

    % UIWAIT makes lvd_editThrottleModifierProfileElementGUI wait for user response (see UIRESUME)
    uiwait(handles.lvd_editThrottleModifierProfileElementGUI);

function populateUI(elem, allowFuPctEdit, curve, handles)
    handles.titleLabel.String = sprintf('Edit %s Element', curve.getCurveName());

    handles.indepValLabel.String = elem.getIndepVarName();
    handles.fuRemainPctText.String = fullAccNum2Str(elem.indepVar);
    handles.indepUnitLbl.String = elem.getIndepVarUnit();

    handles.depValLabel.String = elem.getDepVarName();
    handles.throtModifierText.String = fullAccNum2Str(elem.depVar);
    handles.depUnitLbl.String = elem.getDepVarUnit();

    if(allowFuPctEdit == false)
        handles.fuRemainPctText.Enable = 'off';
    end

% --- Outputs from this function are returned to the command line.
function varargout = lvd_editThrottleModifierProfileElementGUI_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
    if(isempty(handles))
        varargout{1} = false;
    else
        elem = getappdata(hObject,'elem');
        elem.indepVar = str2double(handles.fuRemainPctText.String);
        elem.depVar = str2double(handles.throtModifierText.String);

        varargout{1} = true;
        close(handles.lvd_editThrottleModifierProfileElementGUI);
    end

% --- Executes on button press in saveAndCloseButton.
function saveAndCloseButton_Callback(hObject, eventdata, handles)
% hObject    handle to saveAndCloseButton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
    errMsg = validateInputs(handles);

    if(isempty(errMsg))
        uiresume(handles.lvd_editThrottleModifierProfileElementGUI);
    else
        msgbox(errMsg,'Invalid Inputs','error');
    end

function errMsg = validateInputs(handles)
    errMsg = {};

    elem = getappdata(handles.lvd_editThrottleModifierProfileElementGUI,'elem');

    val = str2double(get(handles.fuRemainPctText,'String'));
    enteredStr = get(handles.fuRemainPctText,'String');
    numberName = elem.getIndepVarName();
    lb = elem.minIndepValue;
    ub = elem.maxIndepValue;
    isInt = false;
    errMsg = validateNumber(val, numberName, lb, ub, isInt, errMsg, enteredStr);

    if(isempty(errMsg))
        if((val == elem.minIndepValue || val == elem.maxIndepValue) && elem.indepVar ~= val)
            errMsg = {sprintf('Cannot add points at %.1f %s or %.1f %s.  Edit these points from the existing list if necessary.', elem.minIndepValue, elem.maxIndepValue, elem.getIndepVarUnit(), elem.getDepVarUnit())};
        end
    end

    val = str2double(get(handles.throtModifierText,'String'));
    enteredStr = get(handles.throtModifierText,'String');
    numberName = elem.getDepVarName();
    lb = elem.minDepValue;
    ub = elem.maxDepValue;
    isInt = false;
    errMsg = validateNumber(val, numberName, lb, ub, isInt, errMsg, enteredStr);

% --- Executes on button press in cancelButton.
function cancelButton_Callback(hObject, eventdata, handles)
% hObject    handle to cancelButton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
    close(handles.lvd_editThrottleModifierProfileElementGUI);

function fuRemainPctText_Callback(hObject, eventdata, handles)
% hObject    handle to fuRemainPctText (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of fuRemainPctText as text
%        str2double(get(hObject,'String')) returns contents of fuRemainPctText as a double
    newInput = get(hObject,'String');
    newInput = attemptStrEval(newInput);
    set(hObject,'String', newInput);

% --- Executes during object creation, after setting all properties.
function fuRemainPctText_CreateFcn(hObject, eventdata, handles)
% hObject    handle to fuRemainPctText (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function throtModifierText_Callback(hObject, eventdata, handles)
% hObject    handle to throtModifierText (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of throtModifierText as text
%        str2double(get(hObject,'String')) returns contents of throtModifierText as a double
    newInput = get(hObject,'String');
    newInput = attemptStrEval(newInput);
    set(hObject,'String', newInput);

% --- Executes during object creation, after setting all properties.
function throtModifierText_CreateFcn(hObject, eventdata, handles)
% hObject    handle to throtModifierText (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --- Executes on key press with focus on lvd_editThrottleModifierProfileElementGUI or any of its controls.
function lvd_editThrottleModifierProfileElementGUI_WindowKeyPressFcn(hObject, eventdata, handles)
% hObject    handle to lvd_editThrottleModifierProfileElementGUI (see GCBO)
% eventdata  structure with the following fields (see MATLAB.UI.FIGURE)
%	Key: name of the key that was pressed, in lower case
%	Character: character interpretation of the key(s) that was pressed
%	Modifier: name(s) of the modifier key(s) (i.e., control, shift) pressed
% handles    structure with handles and user data (see GUIDATA)
    switch(eventdata.Key)
        case 'return'
            saveAndCloseButton_Callback(handles.saveAndCloseButton, [], handles);
        case 'enter'
            saveAndCloseButton_Callback(handles.saveAndCloseButton, [], handles);
        case 'escape'
            close(handles.lvd_editThrottleModifierProfileElementGUI);
    end


function MRSIStruct = setVoxelSize(MRSIStruct, dimension, value)
    switch(lower(dimension))
        case('x')
            MRSIStruct.voxelSize.x = value;
        case('y')
            MRSIStruct.voxelSize.y = value;
        case('z')
            MRSIStruct.voxelSize.z = value;
    end
end


function h = getCERRHandle(h_name,stateS)
%function h = getCERRHandle(h_name,stateS)
%Retrieve a handle name from the state structure, stateS.
%JOD, 28 Oct 02.
%
% 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/>.

h = [];
if ~isempty(stateS.handle) & isfield(stateS.handle, h_name)
  h = getfield(stateS.handle, h_name);
end


mex -lopencv_core -lopencv_imgproc -L/usr/local/lib -I/usr/local/include/ resize.cpp MxArray.cpp
mex -lopencv_core -lopencv_imgproc -L/usr/local/lib -I/usr/local/include/ calcIIF.cpp MxArray.cpp
mex im2colstep.c


%-----------------------------------------------------------------------
% Job saved on 01-Dec-2015 13:23:09 by cfg_util (rev $Rev: 6627 $)
% spm SPM - SPM12 (12.1)
% cfg_basicio BasicIO - Unknown
% dtijobs DTI tools - Unknown
% impexp_NiftiMrStruct NiftiMrStruct - Unknown
% cfg_logextract LogExt - Unknown
% tbx_dpc dPC Tools - Unknown
% prt PRoNTo - Unknown
% perfusion Perfusion - Unknown
% menu_cfg ConfGUI - Unknown
%-----------------------------------------------------------------------
matlabbatch{1}.menu_cfg.menu_entry.conf_files.type = 'cfg_files';
matlabbatch{1}.menu_cfg.menu_entry.conf_files.name = 'File Set';
matlabbatch{1}.menu_cfg.menu_entry.conf_files.tag = 'files';
matlabbatch{1}.menu_cfg.menu_entry.conf_files.filter = '\.gz$';
matlabbatch{1}.menu_cfg.menu_entry.conf_files.ufilter = '.*';
matlabbatch{1}.menu_cfg.menu_entry.conf_files.dir = '';
matlabbatch{1}.menu_cfg.menu_entry.conf_files.num = [0 Inf];
matlabbatch{1}.menu_cfg.menu_entry.conf_files.check = [];
matlabbatch{1}.menu_cfg.menu_entry.conf_files.rewrite_job = [];
matlabbatch{1}.menu_cfg.menu_entry.conf_files.help = {'Select a set of files.'};
matlabbatch{1}.menu_cfg.menu_entry.conf_files.def = [];
matlabbatch{2}.menu_cfg.menu_entry.conf_files.type = 'cfg_files';
matlabbatch{2}.menu_cfg.menu_entry.conf_files.name = 'Output directory';
matlabbatch{2}.menu_cfg.menu_entry.conf_files.tag = 'outdir';
matlabbatch{2}.menu_cfg.menu_entry.conf_files.filter = 'dir';
matlabbatch{2}.menu_cfg.menu_entry.conf_files.ufilter = '.*';
matlabbatch{2}.menu_cfg.menu_entry.conf_files.dir = [];
matlabbatch{2}.menu_cfg.menu_entry.conf_files.num = [0 1];
matlabbatch{2}.menu_cfg.menu_entry.conf_files.check = [];
matlabbatch{2}.menu_cfg.menu_entry.conf_files.rewrite_job = [];
matlabbatch{2}.menu_cfg.menu_entry.conf_files.help = {'Output files will be placed in this folder. Leave empty to put them into the same folder as the original files.'};
matlabbatch{2}.menu_cfg.menu_entry.conf_files.def = [];
matlabbatch{3}.menu_cfg.menu_entry.conf_menu.type = 'cfg_menu';
matlabbatch{3}.menu_cfg.menu_entry.conf_menu.name = 'Keep original files';
matlabbatch{3}.menu_cfg.menu_entry.conf_menu.tag = 'keep';
matlabbatch{3}.menu_cfg.menu_entry.conf_menu.labels = {
                                                       'Yes'
                                                       'No'
                                                       }';
matlabbatch{3}.menu_cfg.menu_entry.conf_menu.values = {
                                                       true
                                                       false
                                                       }';
matlabbatch{3}.menu_cfg.menu_entry.conf_menu.check = [];
matlabbatch{3}.menu_cfg.menu_entry.conf_menu.rewrite_job = [];
matlabbatch{3}.menu_cfg.menu_entry.conf_menu.help = {};
matlabbatch{3}.menu_cfg.menu_entry.conf_menu.def = [];
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.type = 'cfg_exbranch';
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.name = 'Gunzip Files';
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.tag = 'cfg_gunzip_files';
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.val{1}(1) = cfg_dep('Files: File Set (cfg_files)', substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1}));
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.val{2}(1) = cfg_dep('Files: Output directory (cfg_files)', substruct('.','val', '{}',{2}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1}));
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.val{3}(1) = cfg_dep('Menu: Keep original files (cfg_menu)', substruct('.','val', '{}',{3}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1}));
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.prog = @cfg_run_gunzip_files;
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.vout = @cfg_vout_gunzip_files;
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.check = [];
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.rewrite_job = [];
matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.help = {'Gunzip each file in a set of files.'};


function outd = figmenus(d)
% Set up sliceomatic's gui menus within structure D

% Main Figure Menu
  set(gcf,'menubar','none');

  % File menu
  d.filemenu = uimenu(gcf,'label','File');
  d.fcopy = uimenu(d.filemenu, 'label', 'Copy figure','callback', 'sliceomatic copy');
  d.fprint  = uimenu(d.filemenu,'label','Print...','callback','sliceomatic print');
  % How do get these props onto the print figure?
  %d.fprints = uimenu(d.filemenu,'label','Print Setup...','callback','printdlg -setup');
  % ---
  d.fexit = uimenu(d.filemenu, 'label', 'Close','callback','closereq',...
                   'separator','on');

  % Controls Menu
  d.defcontrols = uimenu(gcf,'label','Controls', 'callback',@controlmenu);
  d.camtoolbar = uimenu(d.defcontrols,'label','Camera toolbar','callback', 'sliceomatic cameratoolbar');
  d.dcalpha = uimenu(d.defcontrols,'label','Controls Transparency');
  d.dcalpha1= uimenu(d.dcalpha,'label','1','callback','sliceomatic controlalpha 1');
  d.dcalpha8= uimenu(d.dcalpha,'label','.8','callback','sliceomatic controlalpha .8');
  d.dcalpha6= uimenu(d.dcalpha,'label','.6','callback','sliceomatic controlalpha .6');
  d.dcalpha5= uimenu(d.dcalpha,'label','.5','callback','sliceomatic controlalpha .5');
  d.dcalpha4= uimenu(d.dcalpha,'label','.4','callback','sliceomatic controlalpha .4');
  d.dcalpha2= uimenu(d.dcalpha,'label','.2','callback','sliceomatic controlalpha .2');
  d.dcalpha0= uimenu(d.dcalpha,'label','0','callback','sliceomatic controlalpha 0');
  d.dclabels= uimenu(d.defcontrols','label','Tick Labels','callback','sliceomatic controllabels');
  d.dcvis   = uimenu(d.defcontrols','label','Visible','callback','sliceomatic controlvisible');
  %  d.dcslice = uimenu(d.defcontrols,'label','Slice Controls','callback','sliceomatic useslicecontrols');
%  d.dciso   = uimenu(d.defcontrols,'label','Iso Surface Control','callback','sliceomatic useisocontrols','separator','on');

  % Remove this once we have more controls to enable and disable.
%  set(d.defcontrols,'vis','off');

  % Default for new slices menu
  d.defmenu = uimenu(gcf,'label','Object_Defaults', 'callback', @defaultmenu);
  d.dfacet  = uimenu(d.defmenu,'label','Slice Color Faceted','callback','sliceomatic defaultfaceted');
  d.dflat   = uimenu(d.defmenu,'label','Slice Color Flat',   'callback','sliceomatic defaultflat');
  d.dinterp = uimenu(d.defmenu,'label','Slice Color Interp', 'callback','sliceomatic defaultinterp');
  d.dtex    = uimenu(d.defmenu,'label','Slice Color Texture','callback','sliceomatic defaulttexture');
  d.dcnone  = uimenu(d.defmenu,'label','Slice Color None','callback','sliceomatic defaultcolornone');
  d.dtnone  = uimenu(d.defmenu,'label','Slice Transparency None','callback','sliceomatic defaulttransnone','separator','on');
  d.dtflat  = uimenu(d.defmenu,'label','Slice Transparency Flat','callback','sliceomatic defaulttransflat');
  d.dtinterp= uimenu(d.defmenu,'label','Slice Transparency Interp','callback','sliceomatic defaulttransinterp');
  d.dttex   = uimenu(d.defmenu,'label','Slice Transparency Texture','callback','sliceomatic defaulttranstexture');
  d.dlflat  = uimenu(d.defmenu,'label','IsoSurface Lighting Flat','callback','sliceomatic defaultlightflat','separator','on');
  d.dlsmooth= uimenu(d.defmenu,'label','IsoSurface Lighting Smooth','callback','sliceomatic defaultlightsmooth');
  d.dcflat  = uimenu(d.defmenu,'label','Contour Color Flat',   'callback','sliceomatic defaultcontourflat','separator','on');
  d.dcinterp= uimenu(d.defmenu,'label','Contour Color Interp', 'callback','sliceomatic defaultcontourinterp');
  d.dcblack = uimenu(d.defmenu,'label','Contour Color Black',  'callback','sliceomatic defaultcontourblack');
  d.dcwhite = uimenu(d.defmenu,'label','Contour Color White',  'callback','sliceomatic defaultcontourwhite');
  d.dclinew = uimenu(d.defmenu,'label','Contour Line Width');
  d.dcl1    = uimenu(d.dclinew,'label','1','callback','sliceomatic defaultcontourlinewidth 1');
  d.dcl2    = uimenu(d.dclinew,'label','2','callback','sliceomatic defaultcontourlinewidth 2');
  d.dcl3    = uimenu(d.dclinew,'label','3','callback','sliceomatic defaultcontourlinewidth 3');
  d.dcl4    = uimenu(d.dclinew,'label','4','callback','sliceomatic defaultcontourlinewidth 4');
  d.dcl5    = uimenu(d.dclinew,'label','5','callback','sliceomatic defaultcontourlinewidth 5');
  d.dcl6    = uimenu(d.dclinew,'label','6','callback','sliceomatic defaultcontourlinewidth 6');

  d.defcolor='texture';
  d.defalpha='none';
  d.deflight='smooth';
  d.defcontourcolor='black';
  d.defcontourlinewidth=1;

  % Set props for all slices menu
  d.allmenu = uimenu(gcf,'label','AllSlices');
  uimenu(d.allmenu,'label','Color Faceted','callback','sliceomatic allfacet');
  uimenu(d.allmenu,'label','Color Flat','callback','sliceomatic allflat');
  uimenu(d.allmenu,'label','Color Interp','callback','sliceomatic allinterp');
  uimenu(d.allmenu,'label','Color Texture','callback','sliceomatic alltex');
  uimenu(d.allmenu,'label','Color None','callback','sliceomatic allnone');
  uimenu(d.allmenu,'label','Transparency None','callback','sliceomatic alltnone','separator','on');
  uimenu(d.allmenu,'label','Transparency .5','callback','sliceomatic alltp5');
  uimenu(d.allmenu,'label','Transparency Flat','callback','sliceomatic alltflat');
  uimenu(d.allmenu,'label','Transparency Interp','callback','sliceomatic alltinterp');
  uimenu(d.allmenu,'label','Transparency Texture','callback','sliceomatic allttex');

  % Set Help menu
  d.Help = uimenu(gcf,'label','Help');
  uimenu(d.Help,'label','About dicomrt_explore','callback','sliceomatic showabout_dicomrt_explore');
  uimenu(d.Help,'label','About sliceomatic','callback','sliceomatic showabout_sliceomatic');

  % Context Menus
  % Slice Context Menu
  d.uic=uicontextmenu('callback', @slicecontextmenu);
  d.vistog = uimenu(d.uic,'label','Visible','callback','sliceomatic togglevisible');
  d.uicdelete = uimenu(d.uic,'label','Delete','callback','sliceomatic deleteslice');
  d.smfacet   = uimenu(d.uic,'label','Color Faceted','callback','sliceomatic setfaceted','separator','on');
  d.smflat    = uimenu(d.uic,'label','Color Flat','callback','sliceomatic setflat');
  d.sminterp  = uimenu(d.uic,'label','Color Interp','callback','sliceomatic setinterp');
  d.smtex     = uimenu(d.uic,'label','Color Texture','callback','sliceomatic settexture');
  d.smnone    = uimenu(d.uic,'label','Color None','callback','sliceomatic setnone');
  d.smtnone   = uimenu(d.uic,'label','Transparency None','callback','sliceomatic setalphanone','separator','on');
  d.smtp5     = uimenu(d.uic,'label','Transparency .5','callback','sliceomatic setalphapoint5');
  d.smtflat   = uimenu(d.uic,'label','Transparency Flat','callback','sliceomatic setalphaflat');
  d.smtinterp = uimenu(d.uic,'label','Transparency Interp','callback','sliceomatic setalphainterp');
  d.smttex    = uimenu(d.uic,'label','Transparency Texture','callback','sliceomatic setalphatexture');
  d.smcontour = uimenu(d.uic,'label','Add Contour','separator','on');
  d.smcont0   = uimenu(d.smcontour,'label','Auto','callback','sliceomatic slicecontour');
  d.smcont1   = uimenu(d.smcontour,'label','Select','callback','sliceomatic slicecontour_select','separator','on');
  d.smrcontour= uimenu(d.uic,'label','Remove Contour','callback','sliceomatic deleteslicecontour');
  d.smcflat   = uimenu(d.uic,'label','Contour Flat','callback','sliceomatic slicecontourflat','separator','on');
  d.smcinterp = uimenu(d.uic,'label','Contour Interp','callback','sliceomatic slicecontourinterp');
  d.smcblack  = uimenu(d.uic,'label','Contour Black','callback','sliceomatic slicecontourblack');
  d.smcwhite  = uimenu(d.uic,'label','Contour White','callback','sliceomatic slicecontourwhite');
  d.smccolor  = uimenu(d.uic,'label','Contour Color','callback','sliceomatic slicecontourcolor');
  d.smclinew  = uimenu(d.uic,'label','Contour Line Width');
  d.smcl1     = uimenu(d.smclinew,'label','1','callback','sliceomatic slicecontourlinewidth 1');
  d.smcl2     = uimenu(d.smclinew,'label','2','callback','sliceomatic slicecontourlinewidth 2');
  d.smcl3     = uimenu(d.smclinew,'label','3','callback','sliceomatic slicecontourlinewidth 3');
  d.smcl4     = uimenu(d.smclinew,'label','4','callback','sliceomatic slicecontourlinewidth 4');
  d.smcl5     = uimenu(d.smclinew,'label','5','callback','sliceomatic slicecontourlinewidth 5');
  d.smcl6     = uimenu(d.smclinew,'label','6','callback','sliceomatic slicecontourlinewidth 6');

  % Isosurface Context Menu
  d.uiciso=uicontextmenu('callback',@isocontextmenu);
  d.vistogiso = uimenu(d.uiciso,'label','Visible','callback','sliceomatic isotogglevisible');
  d.isodelete = uimenu(d.uiciso,'label','Delete','callback','sliceomatic isodelete');
  d.isoflatlight=uimenu(d.uiciso,'label','Lighting Flat','callback','sliceomatic isoflatlight','separator','on');
  d.isosmoothlight=uimenu(d.uiciso,'label','Lighting Smooth','callback','sliceomatic isosmoothlight');
  d.isocolor = uimenu(d.uiciso,'label','Change Color','callback','sliceomatic isocolor','separator','on');
  d.isoalpha=uimenu(d.uiciso,'label','Change Transparency');
  uimenu(d.isoalpha,'label','.2','callback','sliceomatic isoalpha .2');
  uimenu(d.isoalpha,'label','.5','callback','sliceomatic isoalpha .5');
  uimenu(d.isoalpha,'label','.8','callback','sliceomatic isoalpha .8');
  uimenu(d.isoalpha,'label','1','callback','sliceomatic isoalpha 1');
  d.isocap=uimenu(d.uiciso,'label','Add IsoCaps','callback','sliceomatic isocaps','separator','on');

  outd = d;

function controlmenu(fig, action)
% Handle doing things to the CONTROLS menu

  d=getappdata(gcf,'sliceomatic');

  if cameratoolbar('getvisible')
    set(d.camtoolbar,'checked','on');
  else
    set(d.camtoolbar,'checked','off');
  end

  set([d.dcalpha1 d.dcalpha8 d.dcalpha6 d.dcalpha5 d.dcalpha6 d.dcalpha2 d.dcalpha0...
       d.dclabels d.dcvis ],...
      'checked','off');

  switch get(d.pxx,'facealpha')
   case 1,  set(d.dcalpha1,'checked','on');
   case .8, set(d.dcalpha8,'checked','on');
   case .6, set(d.dcalpha6,'checked','on');
   case .5, set(d.dcalpha5,'checked','on');
   case .4, set(d.dcalpha4,'checked','on');
   case .2, set(d.dcalpha2,'checked','on');
   case 0,  set(d.dcalpha0,'checked','on');
  end

  if ~isempty(get(d.axx,'xticklabel'))
    set(d.dclabels,'checked','on');
  end

  if strcmp(get(d.axx,'visible'),'on')
    set(d.dcvis,'checked','on');
  end

  if 0
    xt = get(get(d.axx,'title'),'string');
    switch xt
     case 'X Slice Controller'
      set(d.dcslice,'checked','on');
    end

    xt = get(get(d.axiso,'title'),'string');
    switch xt
     case 'Iso Surface Controller'
      set(d.dciso,'checked','on');
    end
  end

function defaultmenu(fig, action)
% Handle toggling bits on the slice defaults menu

  d=getappdata(gcf,'sliceomatic');

  set([d.dfacet d.dflat d.dinterp d.dtex d.dtnone d.dtflat d.dtinterp ...
       d.dttex d.dcflat d.dcinterp d.dcblack d.dcwhite d.dcnone ...
       d.dlflat d.dlsmooth ...
       d.smcl1 d.smcl2 d.smcl3 d.smcl4 d.smcl5 d.smcl6 ], 'checked','off');
  switch d.defcolor
   case 'faceted'
    set(d.dfacet,'checked','on');
   case 'flat'
    set(d.dflat,'checked','on');
   case 'interp'
    set(d.dinterp,'checked','on');
   case 'texture'
    set(d.dtex,'checked','on');
   case 'none'
    set(d.dcnone,'checked','on');
  end
  switch d.defalpha
   case 'none'
    set(d.dtnone,'checked','on');
   case 'flat'
    set(d.dtflat,'checked','on');
   case 'interp'
    set(d.dtinterp,'checked','on');
   case 'texture'
    set(d.dttex,'checked','on');
  end
  switch d.deflight
   case 'flat'
    set(d.dlflat,'checked','on');
   case 'smooth'
    set(d.dlsmooth,'checked','on');
  end
  switch d.defcontourcolor
   case 'flat'
    set(d.dcflat,'checked','on');
   case 'interp'
    set(d.dcinterp,'checked','on');
   case 'black'
    set(d.dcblack,'checked','on');
   case 'white'
    set(d.dcwhite,'checked','on');
  end
  switch d.defcontourlinewidth
   case 1, set(d.dcl1,'checked','on');
   case 2, set(d.dcl2,'checked','on');
   case 3, set(d.dcl3,'checked','on');
   case 4, set(d.dcl4,'checked','on');
   case 5, set(d.dcl5,'checked','on');
   case 6, set(d.dcl6,'checked','on');
  end

function slicecontextmenu(fig,action)
% Context menu state for slices

  d=getappdata(gcf,'sliceomatic');

  [a s]=getarrowslice;
  set([d.smfacet d.smflat d.sminterp d.smtex d.smtnone d.smtp5 ...
       d.smtflat d.smtinterp d.smttex d.smnone],'checked','off');
  set(d.vistog,'checked',get(s,'visible'));

  if propcheck(s,'edgec',[0 0 0])
    set(d.smfacet,'checked','on');
  elseif propcheck(s,'facec','flat')
    set(d.smflat,'checked','on');
  end
  if propcheck(s,'facec','interp')
    set(d.sminterp,'checked','on');
  end
  if propcheck(s,'facec','texturemap')
    set(d.smtex,'checked','on');
  end
  if propcheck(s,'facec','none')
    set(d.smnone,'checked','on');
  end
  if propcheck(s,'facea',1)
    set(d.smtnone,'checked','on');
  end
  if propcheck(s,'facea',.5)
    set(d.smtp5,'checked','on');
  end
  if propcheck(s,'facea','flat')
    set(d.smtflat,'checked','on');
  end
  if propcheck(s,'facea','interp')
    set(d.smtinterp,'checked','on');
  end
  if propcheck(s,'facea','texturemap')
    set(d.smttex,'checked','on');
  end
  cm = [d.smcflat d.smcinterp d.smcblack d.smcwhite d.smccolor ...
       d.smcl1 d.smcl2 d.smcl3 d.smcl4 d.smcl5 d.smcl6 ];
  set(cm,'checked','off');
  if isempty(getappdata(s,'contour'))
    set(d.smcontour,'enable','on');
    set(d.smrcontour,'enable','off');
    set(cm,'enable','off');
  else
    set(d.smcontour,'enable','off')
    set(d.smrcontour,'enable','on')
    set(cm,'enable','on')
    c = getappdata(s,'contour');
    ec = get(c,'edgecolor');
    if isa(ec,'char')
      switch ec
       case 'flat'
        set(d.smcflat,'checked','on');
       case 'interp'
        set(d.smcinterp,'checked','on');
      end
    else
      if ec == [ 1 1 1 ]
        set(d.smcwhite,'checked','on');
      elseif ec == [ 0 0 0 ]
        set(d.smcblack,'checked','on');
      else
        set(d.smccolor,'checked','on');
      end
    end
    clw = get(c,'linewidth');
    switch clw
     case 1, set(d.smcl1,'checked','on');
     case 2, set(d.smcl2,'checked','on');
     case 3, set(d.smcl3,'checked','on');
     case 4, set(d.smcl4,'checked','on');
     case 5, set(d.smcl5,'checked','on');
     case 6, set(d.smcl6,'checked','on');
    end
  end

function isocontextmenu(fig,action)
% Context menu state for isosurfaces

  d=getappdata(gcf,'sliceomatic');

  [a s]=getarrowslice;
  if propcheck(s,'facelighting','flat')
    set(d.isoflatlight,'checked','on');
    set(d.isosmoothlight,'checked','off');
  else
    set(d.isoflatlight,'checked','off');
    set(d.isosmoothlight,'checked','on');
  end
  set(d.vistogiso,'checked',get(s,'visible'));
  if ~isempty(getappdata(s,'isosurfacecap'))
    set(d.isocap,'checked','on');
  else
    set(d.isocap,'checked','off');
  end


function checkForCharUnitsInGUI(hGUI)
%checkForCharUnitsInGUI Summary of this function goes here
%   Detailed explanation goes here
    child_handles = allchild(hGUI);
    for(i=1:length(child_handles))
        hChild = child_handles(i);
        try
            unit = get(hChild,'Units');
            if(~strcmpi(unit,'pixels') && ~strcompi(unit,'data'))
                tag = get(hChild,'Tag');
                disp(['Unit of ', tag, ' is set to ', unit, '!']);
            end
        catch ME

        end
        checkForCharUnitsInGUI(hChild);
    end
end


function handleFileName(obj)

%--IF EMPTY FILENAME, CHECK FOR PLOT TITLES--%
if isempty(obj.PlotOptions.FileName)
    for t = 1:obj.State.Figure.NumTexts
        if obj.State.Text(t).Title
            str = get(obj.State.Text(t).Handle,'String');
            interp = get(obj.State.Text(t).Handle,'Interpreter');
            obj.PlotOptions.FileName = parseString(str,interp);
        end
    end
end

%--IF FILENAME IS STILL EMPTY SET TO UNTITLED--%
if isempty(obj.PlotOptions.FileName)
    obj.PlotOptions.FileName = 'untitled';
end

end


function K = k00(x, xp, hyp, ubar, ubarp, dt, i)

K = k00.ku0u0(x, xp, hyp, ubar, ubarp, dt, i);

end


function [ meta ] = read_nitf_meta( filename )
%READ_NITF_META Read metadata from NITF into a structure
%
%   NITFHDR = READ_NITF_META(NITFFILE) calls functions to parse NITF file
%   segments.
%
% Written by: Matt Donath, NGA, matthew.b.donath@nga.ic.gov
% Modified by: Wade Schwartzkopf, NGA/IDT
% References:
%  > MIL-STD-2500A, NATIONAL IMAGERY TRANSMISSION FORMAT VERSION 2.0
%  > MIL-STD-2500C, NATIONAL IMAGERY TRANSMISSION FORMAT VERSION 2.1
%  > STDI-0001,     NATIONAL SUPPORT DATA EXTENSIONS (SDE)(VERSION 1.3/CN2)
%                   FOR THE NATIONAL IMAGERY TRANSMISSION FORMAT (NITF)
%  > STDI-0002,     THE COMPENDIUM OF CONTROLLED EXTENSIONS (CE) FOR THE
%                   NATIONAL IMAGERY TRANSMISSION FORMAT (NITF) VERSION 2.1
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

%% OPEN FILE
fid=fopen(filename,'r','b'); % NITF always big-endian

%% READ NITF FILE HEADER
meta.filehdr = read_nitf_filehdr(fid);

%% READ IMAGE SEGMENTS
nextImgSubhdroffset=meta.filehdr.HL;
for i = 1:meta.filehdr.NUMI
    fseek(fid,nextImgSubhdroffset,'bof'); % Jump to ith image segment
    meta.imagesubhdr{i}=read_nitf_imgsubhdr(fid);
    nextImgSubhdroffset=nextImgSubhdroffset+meta.filehdr.LISH(i)+meta.filehdr.LI(i);
end
fseek(fid,nextImgSubhdroffset,'bof'); % Jump to end of image segments

%% SKIP GRAPHIC SEGMENTS
if isfield(meta.filehdr,'NUMS') && meta.filehdr.NUMS > 0
    for seg = 1:meta.filehdr.NUMS
        fseek(fid, meta.filehdr.LSSH(seg), 'cof'); % Skip graphic segment subheaders
        fseek(fid, meta.filehdr.LS(seg), 'cof'); % Skip graphic segment data
    end
end

%% SKIP RESERVED SEGMENTS
if isfield(meta.filehdr,'NUMX') && meta.filehdr.NUMX > 0
    for seg = 1:meta.filehdr.NUMX
        fseek(fid, meta.filehdr.LXSH(seg), 'cof'); % Skip reserved segment subheaders
        fseek(fid, meta.filehdr.LX(seg), 'cof'); % Skip reserved segment data
    end
end

%% SKIP LABEL SEGMENTS
if isfield(meta.filehdr,'NUML') && meta.filehdr.NUML > 0
    for seg = 1:meta.filehdr.NUML
        fseek(fid, meta.filehdr.LLSH(seg), 'cof'); % Skip label segment subheaders
        fseek(fid, meta.filehdr.LL(seg), 'cof'); % Skip label segment data
    end
end

%% SKIP TEXT SEGMENTS
if isfield(meta.filehdr,'NUMT') && meta.filehdr.NUMT > 0
    for seg = 1:meta.filehdr.NUMT
%         % Skip text segments
%         fseek(fid, meta.filehdr.LTSH(seg), 'cof'); % Skip text segment subheaders
%         fseek(fid, meta.filehdr.LT(seg), 'cof'); % Skip text segment data
        % Read text segments
        meta.txtsubhdr{seg}=read_nitf_txtsubhdr(fid);
        meta.txtseg{seg} = fread(fid,meta.filehdr.LT(seg),'uint8=>char')';
    end
end

%% READ DATA EXTENSION SEGMENT SUBHEADERS
curr_pos = ftell(fid);
if isfield(meta.filehdr,'NUMDES') && meta.filehdr.NUMDES > 0
    for seg = 1:meta.filehdr.NUMDES
        fseek(fid, curr_pos, 'bof');
        fld = strcat('DESSubhdr',num2str(seg));
        meta = readDESegSubhdr(meta,fid,fld);
        curr_pos = curr_pos + meta.filehdr.LDSH(seg) + meta.filehdr.LD(seg);
    end
end

%% READ RESERVED EXTENSION SUBHEADERS
if isfield(meta.filehdr,'NUMRES') && meta.filehdr.NUMRES > 0
    for seg = 1:meta.filehdr.NUMRES
        fld = strcat('RESSubhdr',num2str(seg));
        meta = readRESegSubhdr(meta,fid,fld);
    end
end

%% Close file
fclose(fid);

end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////