function nameList = makeNamesUnique(nameList, varargin)
% Identifies duplicate names in a list of names (cell array)
% and prompts the user to input new names.
% Called by `cleanTmodel`, `verifyModel`, `addSEEDInfo`, calls `countUnique`.
%
% USAGE:
%
%    nameList = makeNamesUnique(nameList, [nameInfo])
%
% INPUTS:
%    nameList:    Cell array of entires that contains duplicates.
%
% OPTIONAL INPUTS:
%    nameInfo:    Cell array the same length as nameList that contains
%                 information pertaining to the matching entry in `nameList`. This
%                 array can help the renaming process.
%
% OUTPUTS:
%    nameList:    Version of nameList with all unique entires.
%
% Please cite:
% `Sauls, J. T., & Buescher, J. M. (2014). Assimilating genome-scale
% metabolic reconstructions with modelBorgifier. Bioinformatics
% (Oxford, England), 30(7), 1036?8`. http://doi.org/10.1093/bioinformatics/btt747
%
% ..
%    Edit the above text to modify the response to help addMetInfo
%    Last Modified by GUIDE v2.5 06-Dec-2013 14:19:28
%    This file is published under Creative Commons BY-NC-SA.
%
%    Correspondance:
%    johntsauls@gmail.com
%
%    Developed at:
%    BRAIN Aktiengesellschaft
%    Microbial Production Technologies Unit
%    Quantitative Biology and Sequencing Platform
%    Darmstaeter Str. 34-36
%    64673 Zwingenberg, Germany
%    www.brain-biotech.de

if (nargin > 1) % Declare variables.
    nameInfo = varargin{1} ;
end

% List of unique names, sorted.
uniqList = unique(nameList) ;

% Automatically rename entities?
[names, cnt] = countUnique(nameList);
nameInd = find(cnt > 1);
if ~isempty(nameInd)

    fprintf('%d non-unique names. ', length(nameInd))
    renameFlag = input(['Rename automatically?\n' ...
                     'Otherwise manual renaming will proceed. (y/n): '],...
                      's') ;
    if strcmpi(renameFlag, 'y') || strcmpi(renameFlag, 'yes')
        for i = 1:length(nameInd)
           thisName = names{nameInd(i)} ;
           fprintf('Renaming non-unique name %s (%d instances): ',...
                thisName,cnt(nameInd(i)));
           searchString = regexprep(thisName, '\[', '\\[') ;
           searchString = regexprep(searchString,'\]', '\\]') ;
           searchString = ['^' searchString '$'] ;
           IDs = find(~cellfun(@isempty, regexp(nameList, searchString))) ;
           for j = 1:length(IDs)
               if regexp(nameList{IDs(j)}, '\[\w\]$')
                   nameList{IDs(j)} = [thisName(1:end - 3) '_' num2str(j) ...
                                       thisName(end - 2:end)] ;
               else
                   nameList{IDs(j)} = [thisName '_' num2str(j)] ;
               end
               fprintf('%s\t', nameList{IDs(j)}) ;
           end
           fprintf('\n')
        end
    end
end

%% Find duplicate names.
for iName = 1:length(uniqList)
    dupPos = find(strcmp(uniqList{iName}, nameList)) ;
    % If there are duplicates.
    if length(dupPos) > 1
        dupNo = length(dupPos) ;
        % Duplicate name
        dupName = uniqList{iName} ;
        fprintf('%s has duplicates:\n', dupName)
        % Go through each duplicate and print info.
        for iDup = 1:dupNo
            if exist('nameInfo','var')
                string = [num2str(dupPos(iDup)), '\t', ...
                          nameInfo{dupPos(iDup)}, '\n'] ;
                fprintf(string)
            else
                fprintf('%d\n', num2str(dupPos(iDup)))
            end
        end
        % Ask for how names should be renamed.
        for iDup = 1:dupNo
            while 1
                prompt = ['Rename ' num2str(dupPos(iDup)) ' as: '] ;
                newName = input(prompt,'s') ;
                alreadytherepos = strcmp(newName, nameList) ;
                alreadytherepos(dupPos) = false ;
                % Check to see if name is already taken
                if sum(alreadytherepos) == 0
                    nameList{dupPos(iDup)} = newName ;
                    break
                else
                    errorPrompt = 'Name already taken. Proceed (y/n)?: ' ;
                    proceed = input(errorPrompt, 's') ;
                    if strcmpi(proceed, 'y') || strcmpi(proceed, 'yes')
                        nameList{dupPos(iDup)} = newName ;
                        break
                    else
                        continue
                    end
                end
            end
        end
    end
end

%% Check to make sure it worked.
if length(unique(nameList)) ~= length(nameList)
    fprintf('ERROR: Duplicate names still exist!\n')
end


function [steeringModel, ok] = promptForSteeringModelType(selectedEnum)
    [listboxStr, enums] = SteerModelTypeEnum.getListBoxStr();

    if(not(isempty(selectedEnum)))
        initialValue = find(selectedEnum == enums, 1, 'first');

        if(isempty(initialValue))
            initialValue = 1;
        end
    else
        initialValue = 1;
    end

    out = AppDesignerGUIOutput();
    listdlgARH_App('ListString',listboxStr, ...
                    'Name','Select Steering Model Type', ...
                    'PromptString',{'Select steering model type:'}, ...
                    'SelectionMode','single', ...
                    'ListSize',[300 300], ...
                    'InitialValue', initialValue, ...
                    'out',out);
    Selection = out.output{1};
    ok = out.output{2};

    if(ok)
        enum = SteerModelTypeEnum.getEnumForListboxStr(listboxStr{Selection});

        switch enum
            case SteerModelTypeEnum.PolyAngles
                steeringModel = RollPitchYawPolySteeringModel.getDefaultSteeringModel();

            case SteerModelTypeEnum.QuaterionInterp
                steeringModel = GenericQuatInterpSteeringModel.getDefaultSteeringModel();

            case SteerModelTypeEnum.LinearTangentAngles
                steeringModel = GenericLinearTangentSteeringModel.getDefaultSteeringModel();

            case SteerModelTypeEnum.SumOfSinesAngles
                steeringModel = GenericSumOfSinesSteeringModel.getDefaultSteeringModel();

            case SteerModelTypeEnum.SelectableModelAngles
                steeringModel = GenericSelectableSteeringModel.getDefaultSteeringModel();

            otherwise
                error('Unknown steering model type: %s', enum.name);
        end
    else
        steeringModel = RollPitchYawPolySteeringModel.getDefaultSteeringModel();
    end
end


% function [enc]=jp2write(img,filename,indexing,arguments)
%
% It takes a matlab image and stores it in a JPEG2000 compliant file.
% Important notice: the JVM matlab uses is important in order to
% see if jars provided in classpath are compatible or not. If not,
% the only message thrown is 'class not found'.
% Here I've built the jars using -source 1.4 -target 1.4 options in javac
% while it is possible to change the JVM that Matlab is running
% by setting the MATLAB_JAVA system variable. In order to know which
% version of JVM Matlab is running the command is version -java
%
% Using Matlab 7.0.1.24704 (R14) Service Pack 1
% the corresponding JVM is
% Java 1.4.2_05 with Sun Microsystems Inc. Java HotSpot(TM) Client VM
%    (mixed mode)
%
% To resemble the use of the standard command line environment, the
% arguments variable is intented to be a big string with all the
% jj2000 parameters passes as if they were to be driving the command
% line implementation of the encoder.

function enc=jp2write(img,filename,indexing,arguments)

    import middleware.*;
    import jj2000.j2k.util.*;
    import jj2000.j2k.encoder.*;

    % la sistemo sopra l'immagine in modo che qui arrivi sempre RGB

    [image]=im2java(img);

    %imgr=ImgReaderMTLB(image);

    defpl=ParameterList();

    params=Encoder.getAllParameters();

    for i=1:size(params,1)
        if ne(params(i,4),'')
            defpl.setProperty(params(i,1),params(i,4));
            disp(sprintf('%s=%s',char(params(i,1)),char(params(i,4))));
        end
    end

    pl=ParameterList(defpl);

    pl.setProperty('v','on');
    pl.setProperty('debug','on');
    pl.setProperty('Qguard_bits','2'); % default values
    pl.setProperty('file_format','on'); % this to use XML
    pl.setProperty('o',filename); % to resemble matlab

    na=1;
    [t(na).s,r]=strtok(arguments);
    while(~isempty(r))
        na=na+1;
        [t(na).s,r]=strtok(r)
    end
    argv=javaArray('java.lang.String',na);
    for i=1:na
        argv(i)=java.lang.String(t(i).s);

    end
% [PENDING] Gestione degli spazi nei nomi dei file delle ROI
% Serve agire sullo strato di middleware
%     argv(na)=java.lang.String(t(na).s);
%     argv(na)=javaMethod('replaceAll',argv(na),'\*','\\ ')

    pl.parseArgs(argv);

    enc=MyEncoder(ParameterList(pl),image,indexing);

    javaMethod('run',enc);

return


function res = matching( des, varargin )
%MATCHING Run the image matching task
%  RES = MATCHING(DESCRIPTOR) Compute the results for descriptor
%  DESCRIPTOR. Descriptor can either be an object of desc.memdesc or a
%  string, in which case a descriptor from
%
%     `<HBROOT>/data/descriptors/DESCRIPTOR`
%
%  are loaded.
%  Stores the results in csv files in:
%
%      `<HBROOT>/matlab/results/default/DESCRIPTOR/matching.csv`
%
%  If the scores file already exists, skips the computation. Use
%  `'override', true` to change this behaviour.
%
%  Additionally accepts the following 'OptionName', OptionValue arguments:
%
%  'scoresroot' :: '<HBROOT>/matlab/results/default/'
%     Change for a different target path for scores.
%
%  'sequences' :: all of the present for the descriptor
%     Limit the sequences for which the scores are computed (cell array of
%     strings).
%
%  'geom_noise' :: {'easy', 'hard', 'tough'}
%     Limit the geometry noise computed.
%
%  'impairs' :: [1 2; 1 3; 1 4; 1 5; 1 6]
%     Change the image pairs for which the scores are computed.
%
%  'override' :: false
%     Do not overwrite existing score files.

% Copyright (C) 2017 Karel Lenc
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).
if ischar(des), [des, varargin] = desc.memdesc(des, varargin{:}); end;
opts.scoresroot = fullfile(hb_path, 'matlab', 'scores', 'default');
[opts, varargin] = vl_argparse(opts, varargin);
opts.scorespath = fullfile(opts.scoresroot, des.name, 'matching.csv');
opts.sequences = utls.listdirs(des.path);
opts.geom_noise = {'easy', 'hard', 'tough'};
opts.impairs = [1 2; 1 3; 1 4; 1 5; 1 6];
opts.override = false;
opts.verbose = false;
[opts, ~] = vl_argparse(opts, varargin);

if opts.verbose, display(opts); end;
if ~iscell(opts.sequences), opts.sequences  = {opts.sequences}; end;
if ~iscell(opts.geom_noise), opts.geom_noise  = {opts.geom_noise}; end;
vl_xmkdir(fileparts(opts.scorespath));
if ~opts.override && exist(opts.scorespath, 'file')
  res = readtable(opts.scorespath);
  fprintf('Results loaded from %s.\n', opts.scorespath);
  return;
end;

numtasks = numel(opts.sequences) * numel(opts.geom_noise) * size(opts.impairs, 1);
status = utls.textprogressbar(numtasks, 'updatestep', 1, ...
  'startmsg', sprintf('Evaluating "matching" for %s', des.name));
stepi = 1;
res = cell(numtasks, 1);
for gni = 1:numel(opts.geom_noise)
  geom_noise = opts.geom_noise{gni};
  for si = 1:numel(opts.sequences)
    sequence = opts.sequences{si};
    for pi = 1:size(opts.impairs, 1)
      pair = opts.impairs(pi, :);
      descA = des.getimdescs(des, sequence, geom_noise, pair(1));
      descB = des.getimdescs(des, sequence, geom_noise, pair(2));
      res_s = bench.matching.eval(descA, descB);
      res{stepi} = struct(...
        'descriptor', des.name, 'sequence', sequence, ...
        'geom_noise', geom_noise, 'ima', pair(1)-1, 'imb', pair(2)-1, ...
        'ap', res_s.ap, 'auc', res_s.auc, 'sr', res_s.sr);
      status(stepi); stepi = stepi+1;
    end
  end
end
res = struct2table(cell2mat(res), 'AsArray', true);
writetable(res, opts.scorespath);
fprintf('\n');


function [pzout, intv00f, intv00g, intv00h] =  gskpzget(pzinl,intv0ln);
pzout=[];
pzout(1,1:3) = pzinl(intv0ln,1:3);
pzout(2,1:3) = pzinl(intv0ln,4:6);
pzout(3,1:3) = pzinl(intv0ln,7:9);
intv00f = pzinl(intv0ln,10);
intv00g = pzinl(intv0ln,11);
intv00h = pzinl(intv0ln,12);


function [p] = gui_mesh_save(p,mesh_save_command,parent)
% GUI_MESH_OPEN: Load an emse mesh (wfr) file into matlab workspace
%
% Usage: [p] = gui_mesh_save(p,[command],[parent])
%
% p is a structure, generated by 'eeg_toolbox_defaults'
% command is either 'init' or 'save'
% parent is a handle to the gui that calls this gui, useful
% for updating the UserData field of the parent from this gui.
% The p structure is returned to the parent when the parent
% handle is given.
%

% $Revision: 1.1 $ $Date: 2009-04-28 22:13:56 $

% Licence:  GNU GPL, no express or implied warranties
% History:  02/02 Darren.Weber_at_radiology.ucsf.edu
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ~exist('p','var'),
   [p] = eeg_toolbox_defaults;
elseif isempty(p),
   [p] = eeg_toolbox_defaults;
end

if ~exist('mesh_save_command','var'), mesh_save_command = 'init'; end

switch mesh_save_command,

case 'init',

    if exist('parent','var'),
        MESHSave = INIT(p,parent);
    else
        MESHSave = INIT(p);
    end

case 'save',

    MESHSave = get(gcbf,'Userdata');
    gui_mesh_save(MESHSave.p,'init',MESHSave.gui);

    if isequal(get(MESHSave.handles.Bhold,'Value'),1),
       [p] = gui_updateparent(MESHSave,0);
    else
       [p] = gui_updateparent(MESHSave);
        close gcbf;
    end

    fprintf('GUI_MESH_OPEN: Save As not yet implemented\n');

otherwise,

    close gcbf;

end

return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [MESHSave] = INIT(p,parent),

    % GUI General Parameters

    GUIwidth  = 500;
    GUIheight = 120;

    GUI = figure('Name','MESH File Load','Tag','MESH_OPEN',...
                 'NumberTitle','off',...
                 'MenuBar','none','Position',[1 1 GUIwidth GUIheight]);
    movegui(GUI,'center');

    MESHSave.gui = GUI;

    Font.FontName   = 'Helvetica';
    Font.FontUnits  = 'Pixels';
    Font.FontSize   = 12;
    Font.FontWeight = 'normal';
    Font.FontAngle  = 'normal';

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Mesh Data Selection and Parameters

    G.Title_data = uicontrol('Parent',GUI,'Style','text','Units','Normalized',Font, ...
        'Position',[.01 .75 .17 .2],...
        'TooltipString','Mesh/Tesselation Formats (see, ''help mesh_open'')',...
        'String','Mesh Type:','HorizontalAlignment','left');

    switch p.mesh.type
        case 'BrainStorm',       meshType = 1;
        otherwise,               meshType = 1;
    end
    G.PmeshType = uicontrol('Tag','PmeshType','Parent',GUI,'Style','popupmenu',...
        'Units','Normalized',Font,  ...
        'Position',[.20 .75 .28 .2],...
        'String',{'BrainStorm'},'Value',meshType,...
        'TooltipString','Mesh/Tesselation Formats (see, ''help mesh_save'')',...
        'Callback',strcat('MESHSave = get(gcbf,''Userdata'');',...
                          'MESHSave.p.mesh.type = popupstr(MESHSave.handles.PmeshType);',...
                          'set(gcbf,''Userdata'',MESHSave); clear MESHSave;'));

    G.Title_path = uicontrol('Parent',GUI,'Style','text','Units','Normalized',Font, ...
        'Position',[.01 .50 .17 .2],...
        'String','Path','HorizontalAlignment','left');
    G.EmeshPath = uicontrol('Parent',GUI,'Style','edit','Units','Normalized',Font,  ...
    	'Position',[.20 .50 .58 .2], 'String',p.mesh.path,...
        'Callback',strcat('MESHSave = get(gcbf,''Userdata'');',...
                          'MESHSave.p.mesh.path = get(MESHSave.handles.EmeshPath,''String'');',...
                          'set(gcbf,''Userdata'',MESHSave); clear MESHSave;'));

    G.Title_file = uicontrol('Parent',GUI,'Style','text','Units','Normalized',Font, ...
        'Position',[.01 .25 .17 .2],...
        'String','File','HorizontalAlignment','left');
    G.EmeshFile = uicontrol('Parent',GUI,'Style','edit','Units','Normalized',Font,  ...
    	'Position',[.20 .25 .58 .2], 'String',p.mesh.file,...
        'Callback',strcat('MESHSave = get(gcbf,''Userdata'');',...
                          'MESHSave.p.mesh.file = get(MESHSave.handles.EmeshFile,''String'');',...
                          'set(gcbf,''Userdata'',MESHSave); clear MESHSave;'));

    Font.FontWeight = 'bold';

    % BROWSE: Look for the data
    browsecommand = strcat('MESHSave = get(gcbf,''Userdata'');',...
        'cd(MESHSave.p.mesh.path);',...
        '[file, path] = uigetfile(',...
        '{''*.dat;*.txt'', ''ASCII Tesselations (*.dat,*.txt)'';', ...
        ' ''*.asc;*.tri'', ''FreeSurfer Tesselations (*.asc,*.tri)'';', ...
        ' ''*.wfr'', ''EMSE Tesselations (*.wfr)'';', ...
        ' ''*.mat'', ''BrainStorm Tesselations (*.mat)'';', ...
        ' ''*.*'',   ''All Files (*.*)''},', ...
        '''Select Mesh/Tesselation File'');',...
        'if ~isequal(path,0), MESHSave.p.mesh.path = path; end;',...
        'if ~isequal(file,0), MESHSave.p.mesh.file = file; end;',...
        'set(MESHSave.handles.EmeshPath,''String'',MESHSave.p.mesh.path);',...
        'set(MESHSave.handles.EmeshFile,''String'',MESHSave.p.mesh.file);',...
        'if ~isempty(findstr(file,''.dat'')) | ~isempty(findstr(file,''.txt'')), ',...
            'set(MESHSave.handles.PmeshType,''Value'',1); ',...
            'MESHSave.p.mesh.type = popupstr(MESHSave.handles.PmeshType);',...
        'elseif ~isempty(findstr(file,''.asc'')) | ~isempty(findstr(file,''.tri'')), ',...
            'set(MESHSave.handles.PmeshType,''Value'',2); ',...
            'MESHSave.p.mesh.type = popupstr(MESHSave.handles.PmeshType);',...
        'elseif findstr(file,''.wfr''), ',...
            'set(MESHSave.handles.PmeshType,''Value'',3); ',...
            'MESHSave.p.mesh.type = popupstr(MESHSave.handles.PmeshType);',...
        'else, ',...
            'set(MESHSave.handles.PmeshType,''Value'',4); ',...
            'MESHSave.p.mesh.type = popupstr(MESHSave.handles.PmeshType);',...
        'end;',...
        'set(gcbf,''Userdata'',MESHSave); clear MESHSave;');
    G.BmeshFile = uicontrol('Parent',GUI,'Style','pushbutton','Units','Normalized',Font, ...
        'Position',[.01 .01 .17 .2], 'String','BROWSE',...
        'BackgroundColor',[0.8 0.8 0.0],...
        'ForegroundColor', [1 1 1], 'HorizontalAlignment', 'center',...
        'Callback', browsecommand );

    % SAVE AS: Load & convert the data!
    G.Bsave = uicontrol('Parent',GUI,'Style','pushbutton','Units','Normalized', Font, ...
        'Position',[.40 .01 .18 .2],...
        'String','SAVE AS','BusyAction','queue',...
        'TooltipString','Load and convert the mesh (not implemented yet).',...
        'BackgroundColor',[0.0 0.0 0.5],...
        'ForegroundColor', [1 1 1], 'HorizontalAlignment', 'center',...
        'Callback',strcat('MESHSave = get(gcbf,''Userdata'');',...
                          'p = mesh_write(MESHSave.p,''save'');',...
                          'clear MESHSave;'));

    % Cancel
    G.Bcancel = uicontrol('Parent',GUI,'Style','pushbutton','Units','Normalized', Font, ...
        'Position',[.80 .01 .18 .2],...
        'String','CANCEL','BusyAction','queue',...
        'TooltipString','Close, do not return the p struct.',...
        'BackgroundColor',[0.75 0.0 0.0],...
        'ForegroundColor', [1 1 1], 'HorizontalAlignment', 'center',...
        'Callback','gui_mesh_open([],''cancel'');');

    % Store userdata
    if exist('parent','var'), MESHSave.parent.gui = parent; end
    MESHSave.handles = G;
    MESHSave.p = p;
    set(GUI,'HandleVisibility','callback');
    set(GUI,'Userdata',MESHSave);

return


function prefs = editPreferences;
% GUI to set mrVista preference variables.
%
%  prefs = editPreferences;
%
% Returns a prefs struct with each preference field and its current value.
% This is the same struct that would be returned if you ran the command:
%	prefs = getpref('VISTA');
%
% NOTE ABOUT PREFERENCES: The preferences edited here are part of the
% "preferences group" named 'VISTA'; they are only a subset of
% the preferences which may affect mrVista's behavior. For instance,
% the function mrmPreferences will edit a separate set of preferences under
% the 'mesh' preferences group, which determines behavior for dealing with
% surface meshes.
%
% TODO: sub-group the preferences according to related ideas, such as 'File
% Paths', 'Display', 'ROIs', etc. Build a more general GUI to allow the
% user to switch between these preference groups.
%
% ras, 07/2008.

%% get the current values of the preferences
prefs = getpref('VISTA');

%% build the dialog
% (we only build dialogs for certain items; some, for instance, are only
% relevant to mrVista2 GUI behavior.)
dlg(1).fieldName = 'verbose';
dlg(end).style = 'popup';
dlg(end).string = 'How much feedback when using mrVista?';
dlg(end).list = {'0 -- quiet, minimal feedback' ...
				 '1 -- moderate, most status updates, waitbars shown' ...
				 '2 -- verbose, highest level of feedback'};
dlg(end).value = prefsVerboseCheck + 1;

dlg(end+1).fieldName = 'fileFormat';
dlg(end).style = 'popup';
dlg(end).string = 'Save maps / anatomies in which format?';
dlg(end).list = {'default' 'nifti'};
dlg(end).value = prefsFormatCheck;

% this preference only needed for MATLAB <2007a
% (later versions always use JAVA)
v = ver('Matlab');
if str2num(v.Version) < 7.4
	dlg(end+1).fieldName = 'javaOn';
	dlg(end).style = 'checkbox';
	dlg(end).string = 'Use JAVA to render figures';
	if isfield(prefs, dlg(end).fieldName)
		dlg(end).value = prefs.(dlg(end).fieldName);
	else
		dlg(end).value = 0;
	end
end

dlg(end+1).fieldName = 'defaultAnatomyPath';
dlg(end).style = 'edit';
dlg(end).string = 'Default Anatomy Directory (if ''3DAnatomy'' link not present)';
dlg(end).value = prefsDefaultAnatPath;

dlg(end+1).fieldName = 'defaultROIPath';
dlg(end).style = 'edit';
dlg(end).string = 'Default Shared ROI Directory (relative to anatomy directory)';
if isfield(prefs, dlg(end).fieldName)
	dlg(end).value = prefs.(dlg(end).fieldName);
else
	dlg(end).value = 'ROIs';
end

xHairList = {'Regular Crosshairs' 'BrainVoyager style (gap near intersection)'};
dlg(end+1).fieldName = 'xHairMethod';
dlg(end).style = 'popup';
dlg(end).string = 'Method for rendering crosshairs (3-view)?';
dlg(end).list = xHairList;
if isfield(prefs, dlg(end).fieldName)
	dlg(end).value = prefs.(dlg(end).fieldName);
else
	dlg(end).value = 2;
end

dlg(end+1).fieldName = 'xHairColor';
dlg(end).style = 'number';
dlg(end).string = 'Crosshairs color (3-view)?';
if isfield(prefs, dlg(end).fieldName)
	dlg(end).value = prefs.(dlg(end).fieldName)(:)';
else
	dlg(end).value = [1 .5 .5]; % [.8 .8 .6];
end
dlg(end+1).fieldName = 'extendedGrayFields';
dlg(end).style = 'checkbox';
dlg(end).string = 'Gray views: Extended ''leftNodes''/''rightNodes'' fields';
if isfield(prefs, dlg(end).fieldName)
	dlg(end).value = prefs.(dlg(end).fieldName);
else
	dlg(end).value = 1;
end

dlg(end+1).fieldName = 'autoComputeG2VMap';
dlg(end).style = 'checkbox';
dlg(end).string = 'Auto-compute Gray->Vertex Map for Meshes';
if isfield(prefs, dlg(end).fieldName)
	dlg(end).value = prefs.(dlg(end).fieldName);
else
	dlg(end).value = 1;
end

dlg(end+1).fieldName = 'autoComputeV2GMap';
dlg(end).style = 'checkbox';
dlg(end).string = 'Auto-compute Vertex->Gray Map for Meshes';
if isfield(prefs, dlg(end).fieldName)
	dlg(end).value = prefs.(dlg(end).fieldName);
else
	dlg(end).value = 1;
end

dlg(end+1).fieldName = 'savePrefs';
dlg(end).style = 'checkbox';
dlg(end).string = 'Save GUI settings when you close a view window';
if isfield(prefs, dlg(end).fieldName)
	dlg(end).value = prefs.(dlg(end).fieldName);
else
	dlg(end).value = 1;
end

%% put up the dialog; get response
[resp ok] = generalDialog(dlg, 'Set mrVista Preferences', [.25 .25 .5 .5]);

if ~ok
	if prefs.verbose >= 1
		disp('Edit Preferences Canceled.')
		return
	end
end

% parse response for some fields (most should already be in the final
% format)
resp.verbose = cellfind(dlg(1).list, resp.verbose) - 1;
resp.xHairMethod = cellfind(xHairList, resp.xHairMethod);

%% set the preferences based on the dialog
for f = fieldnames(resp)'
	setpref( 'VISTA', f{1}, resp.(f{1}) );
end

% return the updated prefs
prefs = getpref('VISTA');

return


function ndx = read_mat(infilename)
% Reads a struct from a mat file and constructs an Ndx object.
% Inputs:
%   infilename: The name for the mat file to read.
% Outputs:
%   ndx: An Ndx object created from the information in the mat
%     file.

assert(nargin==1)
assert(isa(infilename,'char'))

load(infilename,'ndx');
ndx = Ndx(ndx);

assert(ndx.validate())


%XML_LOAD Loads XML file and return contents as Matlab structure or variable
%
% SYNTAX     V = xml_load( filename )
%            V = xml_load( filename, attswitch )
%
% INPUT
%  filename   filename of xml file to load (if extension .xml is omitted,
%             xml_load tries to append it if the file cannot be found).
%             This file should have been saved with xml_save().
%             If it's a non-Matlab XML file, please use e.g.
%             xmlstr = fileread(filename); V = xml_parseany(xmlstr);
%  attswitch  optional, default='on':
%             'on' takes attributes idx,size,type into account for
%                  creating corresponding Matlab data types
%             'off' ignores attributes in XML element headers
%
% OUTPUT
%   V        Matlab structure or variable
%
% SEE ALSO
%   xml_save, xml_format, xml_formatany, xml_parse, xml_parseany, xml_help, (xmlread, xmlwrite)

% Copyright (C) 2002-2005, University of Southampton
% Author: Dr Marc Molinari <m.molinari@soton.ac.uk>
% $Revision: 1.1 $ $Date: 2005/04/15 17:12:14 $ $Tag$


%Summary simulation logger. Implements simulation logger interface.
%
% Author: Jonathan Karr, jkarr@stanford.edu
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 1/10/2011
classdef SummaryLogger < edu.stanford.covert.cell.sim.util.Logger
    %options
    properties (Access = protected)
        stepSizeSec
        verbosity
        outputDirectory
    end

    %stored data
    properties (SetAccess = protected)
        log
        figureHandle
    end

    properties (Access = protected)
        timer
    end

    %indices into simulation states, processes
    properties (Access = protected)
        stateIndex_time
        stateIndex_mass
        stateIndex_rna
        stateIndex_metabolite
        stateIndex_monomer
        stateIndex_complex
        stateIndex_metabolicReaction
        stateIndex_chromosome
        stateIndex_geometry
        stateIndex_ftsZRing

		processIndex_transcription
		processIndex_translation
        processIndex_replicationInitiation
        processIndex_replication
        processIndex_ftsZPolymerization
    end

    properties(Constant = true)
        SIM_STATUS_DIDNT_START = -3;
        SIM_STATUS_STILL_RUNNING = -2;
        SIM_STATUS_TERMINATED_WITH_ERROR = -1;
        SIM_STATUS_COMPLETED_WITH_DIVISION = 0;
        SIM_STATUS_COMPLETED_WITHOUT_DIVISION = 1;

        SIM_STATUS_INDEX_STATUS = 1;
        SIM_STATUS_INDEX_REP_INIT_TIME = 2;
        SIM_STATUS_INDEX_INIT_REP_TIME = 3;
        SIM_STATUS_INDEX_REP_TIME = 4;
        SIM_STATUS_INDEX_CYTOKINESIS_TIME = 5;
        SIM_STATUS_INDEX_INIT_GROWTH = 6;
        SIM_STATUS_INDEX_FIN_GROWTH = 7;
        SIM_STATUS_INDEX_CUM_GROWTH = 8;
        SIM_STATUS_INDEX_FIN_MASS = 9;
        SIM_STATUS_INDEX_INIT_MASS = 10;
        SIM_STATUS_INDEX_MASS_DOUBLING_TIME = 11;
    end

    %ids
    properties
        ntpWholeCellModelIDs
        aaWholeCellModelIDs
    end

    methods
        function this = SummaryLogger(stepSizeSec, verbosity, outputDirectory)
            if nargin >= 1
                this.stepSizeSec = stepSizeSec;
            else
                this.stepSizeSec = 1;
            end

            if nargin >= 2
                this.verbosity = verbosity;
            else
                this.verbosity = 2;
            end

            if nargin >= 3
                this.outputDirectory = outputDirectory;
            else
                this.outputDirectory = [];
            end
        end
    end

    methods
        function setOptions(this, options)
            metaClass = metaclass(this);
            fields = intersect(fieldnames(options), cellfun(@(x) x.Name, metaClass.Properties, 'UniformOutput',false));
            for i = 1:numel(fields)
                this.(fields{i}) = options.(fields{i});
            end
        end

        %Allocates space for small summary stats gathered after each segment.
        function this = initialize(this, sim)
            %indices
            this.stateIndex_time = sim.stateIndex('Time');
            this.stateIndex_mass = sim.stateIndex('Mass');
            this.stateIndex_rna = sim.stateIndex('Rna');
            this.stateIndex_metabolite = sim.stateIndex('Metabolite');
            this.stateIndex_monomer = sim.stateIndex('ProteinMonomer');
            this.stateIndex_complex = sim.stateIndex('ProteinComplex');
            this.stateIndex_metabolicReaction = sim.stateIndex('MetabolicReaction');
            this.stateIndex_chromosome = sim.stateIndex('Chromosome');
            this.stateIndex_geometry = sim.stateIndex('Geometry');
            this.stateIndex_ftsZRing = sim.stateIndex('FtsZRing');

			this.processIndex_transcription = sim.processIndex('Transcription');
			this.processIndex_translation = sim.processIndex('Translation');
            this.processIndex_replicationInitiation = sim.processIndex('ReplicationInitiation');
            this.processIndex_replication = sim.processIndex('Replication');
            this.processIndex_ftsZPolymerization = sim.processIndex('FtsZPolymerization');

            %ids
            this.ntpWholeCellModelIDs = sim.state('Metabolite').wholeCellModelIDs(sim.state('Metabolite').ntpIndexs);
            this.aaWholeCellModelIDs = sim.state('Metabolite').wholeCellModelIDs(sim.state('Metabolite').aminoAcidIndexs);

            %initialize timer
            this.timer = tic;

            %allocate memory
            nSeg = sim.lengthSec / this.stepSizeSec + 1;
            validateattributes(nSeg, {'numeric'}, {'real', 'positive', 'integer'});

            this.log = struct();
            this.log.runTime               = zeros(1, nSeg, 'double');         %run time of simulation
            this.log.growth                = zeros(1, nSeg, 'double');         %growth rate
            this.log.mass                  = zeros(1, nSeg, 'double');         %cell mass
            this.log.time                  = 0:this.stepSizeSec:sim.lengthSec; %time (seconds)
            this.log.metabolites           = zeros(13, nSeg, 'int32');         %total numbers of ATP, ADP, AMP
            this.log.metaboliteUsages      = zeros(2, nSeg, 'int32');         %total numbers of ATP, ADP, AMP
            this.log.metaboliteProductions = zeros(2, nSeg, 'int32');         %total numbers of ATP, ADP, AMP
            this.log.rnas                  = zeros(7, nSeg, 'int32');          %total numbers of RNA; mature mRNA, rRNA, sRNA, tRNA
            this.log.proteins              = zeros(7, nSeg, 'int32');          %total number of protein monomers and complexes
            this.log.rnaPolymerases        = zeros(1, nSeg, 'int16');          %total number of RNA polymerases
            this.log.ribosomes             = zeros(1, nSeg, 'int16');          %total number of ribosomes
            this.log.supercoiled           = zeros(2, nSeg, 'double');         %number of supercoiled regions
            this.log.dnaA                  = zeros(2, nSeg, 'int16');          %numbers of free and total DnaA monomers
            this.log.dnaAFunctionalBoxes   = zeros(5, nSeg, 'int8');           %polymerization status of R1-5 functional DnaA boxes
            this.log.replisome             = zeros(6, nSeg, 'int32');          %positions of helicase and leading and lagging polymerases
            this.log.ftsZ                  = zeros(2, nSeg, 'int16');          %numbers of free and total FtsZ monomers
            this.log.ftsZRing              = zeros(4, nSeg, 'int32');          %size of FtsZ ring
            this.log.pinchedDiameter       = zeros(1, nSeg, 'double');         %pinched diameter
            this.log.ploidy                = zeros(1, nSeg, 'double');         %ploidy (genetic content--should range from 1 to 2)

            %initialize print, plot
            this.initializePrint();
            this.initializePlot();

            %append log
            this.append(sim);
        end

        %Appends a summary of the current state at a specific index.
        function this = append(this, sim)
            t = sim.states{this.stateIndex_time};
            i = t.values / this.stepSizeSec + 1;
            if mod(i, 1) ~= 0
                return;
            end

            %append log
            this.appendHelper(i, sim);

            %print and plot
            this.print(i);
            this.plot(i);
        end

        function appendHelper(this, i, sim)
            %references
            t = sim.states{this.stateIndex_time};
            mass = sim.states{this.stateIndex_mass};
            r = sim.states{this.stateIndex_rna};
            m = sim.states{this.stateIndex_metabolite};
            pm = sim.states{this.stateIndex_monomer};
            pc = sim.states{this.stateIndex_complex};
            mr = sim.states{this.stateIndex_metabolicReaction};
            c = sim.states{this.stateIndex_chromosome};
            geometry = sim.states{this.stateIndex_geometry};
            ftsZRing = sim.states{this.stateIndex_ftsZRing};

            %indices
            iCytosol = sim.compartment.cytosolIndexs;
            iMembrane = sim.compartment.membraneIndexs;

            %append
            this.log.runTime(1, i) = toc(this.timer); this.timer = tic;
            this.log.time(1, i) = t.values;

            this.log.mass(1, i) = sum(mass.cellDry) / mass.cellInitialDryWeight;
            this.log.growth(1, i) = mr.growth;

            this.log.metabolites(1, i) = sum(m.counts(m.atpIndexs, iCytosol));
            this.log.metabolites(2, i) = sum(m.counts(m.adpIndexs, iCytosol));
            this.log.metabolites(3, i) = sum(m.counts(m.ampIndexs, iCytosol));
            this.log.metabolites(4, i) = sum(m.counts(m.ntpIndexs, iCytosol));
            [this.log.metabolites(5, i), this.log.metabolites(6, i)] = min(m.counts(m.ntpIndexs, iCytosol));
            this.log.metabolites(7, i) = sum(m.counts(m.aminoAcidIndexs(1:20), iCytosol));
            [this.log.metabolites(8, i), this.log.metabolites(9, i)] = min(m.counts(m.aminoAcidIndexs(1:20), iCytosol));
            this.log.metabolites(10,i) = sum(m.counts(m.lipidIndexs, iMembrane));
            this.log.metabolites(11,i) = sum(m.counts(m.carbohydrateIndexs, iCytosol));
            this.log.metabolites(12,i) = sum(m.counts(m.dntpIndexs, iCytosol));
            this.log.metabolites(13,i) = sum(m.counts(m.antibioticIndexs, iCytosol));

            if ~isempty(m.processUsages)
                tmp1 = m.processUsages(sub2ind(size(m.counts), m.ntpIndexs(1), iCytosol), :);
                tmp2 = m.processUsages(sub2ind(size(m.counts), m.ntpIndexs(3), iCytosol), :);
                this.log.metaboliteUsages(1, i) = sum(max(0, -tmp1), 2);
                this.log.metaboliteUsages(2, i) = sum(max(0, -tmp2), 2);
                this.log.metaboliteProductions(1, i) = sum(max(0, tmp1), 2);
                this.log.metaboliteProductions(2, i) = sum(max(0, tmp2), 2);
            end

            this.log.rnas(1, i) = sum(r.counts(:));
            this.log.rnas(2, i) = sum(...
                + r.counts(r.matureIndexs(r.matureMRNAIndexs), iCytosol)...
                + r.counts(r.misfoldedIndexs(r.matureMRNAIndexs), iCytosol)...
                + r.counts(r.damagedIndexs(r.matureMRNAIndexs), iCytosol)...
                );
            this.log.rnas(3, i) = sum(...
                + r.counts(r.matureIndexs(r.matureRRNAIndexs), iCytosol)...
                + r.counts(r.misfoldedIndexs(r.matureRRNAIndexs), iCytosol)...
                + r.counts(r.damagedIndexs(r.matureRRNAIndexs), iCytosol)...
                );
            this.log.rnas(4, i) = sum(...
                + r.counts(r.matureIndexs(r.matureSRNAIndexs), iCytosol) ...
                + r.counts(r.boundIndexs(r.matureSRNAIndexs), iCytosol) ...
                + r.counts(r.aminoacylatedIndexs(r.matureSRNAIndexs), iCytosol) ...
                + r.counts(r.misfoldedIndexs(r.matureSRNAIndexs), iCytosol) ...
                + r.counts(r.damagedIndexs(r.matureSRNAIndexs), iCytosol) ...
                );
            this.log.rnas(5, i) = sum(...
                + r.counts(r.matureIndexs(r.matureTRNAIndexs), iCytosol) ...
                + r.counts(r.aminoacylatedIndexs(r.matureTRNAIndexs), iCytosol) ...
                + r.counts(r.misfoldedIndexs(r.matureTRNAIndexs), iCytosol) ...
                + r.counts(r.damagedIndexs(r.matureTRNAIndexs), iCytosol) ...
                );
            this.log.rnas(6, i) = this.log.rnas(1, i) - sum(this.log.rnas(2:5, i));
            this.log.rnas(7, i) = sum(r.counts(r.damagedIndexs, iCytosol));

            this.log.proteins(1, i) = sum(pm.counts(:));
            this.log.proteins(2, i) = sum(sum(...
                + pm.counts(pm.matureIndexs, :) ...
                + pm.counts(pm.boundIndexs, :) ...
                + pm.counts(pm.inactivatedIndexs, :) ...
                + pm.counts(pm.damagedIndexs, :) ...
                + pm.counts(pm.misfoldedIndexs, :) ...
                ));
            this.log.proteins(3, i) = this.log.proteins(1, i) - this.log.proteins(2, i);
            this.log.proteins(4, i) = sum(pc.counts(:));
            this.log.proteins(5, i) = sum(sum(...
                + pc.counts(pc.matureIndexs, :) ...
                + pc.counts(pc.boundIndexs, :) ...
                + pc.counts(pc.inactivatedIndexs, :) ...
                + pc.counts(pc.damagedIndexs, :) ...
                + pc.counts(pc.misfoldedIndexs, :) ...
                ));
            this.log.proteins(6, i) = this.log.proteins(4, i) - this.log.proteins(5, i);
            this.log.proteins(7, i) = ...
                + sum(sum(pm.counts(pm.damagedIndexs, :))) ...
                + sum(sum(pc.counts(pc.damagedIndexs, :)));

            if this.processIndex_transcription
                transcription = sim.processes{this.processIndex_transcription};
                this.log.rnaPolymerases(1, i) = ...
                    + transcription.enzymes(transcription.enzymeIndexs_rnaPolymerase) ...
                    + transcription.enzymes(transcription.enzymeIndexs_rnaPolymeraseHoloenzyme) ...
                    + transcription.boundEnzymes(transcription.enzymeIndexs_rnaPolymerase) ...
                    + transcription.boundEnzymes(transcription.enzymeIndexs_rnaPolymeraseHoloenzyme);
            end

            if this.processIndex_translation
                translation = sim.processes{this.processIndex_translation};
                this.log.ribosomes(1, i) = ...
                    + translation.enzymes(translation.enzymeIndexs_ribosome30S) ...
                    + translation.enzymes(translation.enzymeIndexs_ribosome30SIF3) ...
                    + translation.enzymes(translation.enzymeIndexs_ribosome50S) ...
                    + translation.enzymes(translation.enzymeIndexs_ribosome70S) ...
                    + translation.boundEnzymes(translation.enzymeIndexs_ribosome30S) ...
                    + translation.boundEnzymes(translation.enzymeIndexs_ribosome30SIF3) ...
                    + translation.boundEnzymes(translation.enzymeIndexs_ribosome50S) ...
                    + translation.boundEnzymes(translation.enzymeIndexs_ribosome70S);
            end

            this.log.supercoiled(1, i) = collapse(c.supercoiled) / 2;
            this.log.supercoiled(2, i) = (collapse(c.linkingNumbers) - collapse(c.polymerizedRegions)/c.relaxedBasesPerTurn) / (collapse(c.polymerizedRegions)/c.relaxedBasesPerTurn);

            if this.processIndex_replicationInitiation
                repInit = sim.processes{this.processIndex_replicationInitiation};

                this.log.dnaA(:, i) = [
                    repInit.enzymes(repInit.enzymeIndexs_DnaA_1mer_ATP)
                    [1 1:7 1:7] * (...
                    + repInit.enzymes([repInit.enzymeIndexs_DnaA; repInit.enzymeIndexs_DnaA_Nmer_ATP; repInit.enzymeIndexs_DnaA_Nmer_ADP]) ...
                    + repInit.boundEnzymes([repInit.enzymeIndexs_DnaA; repInit.enzymeIndexs_DnaA_Nmer_ATP; repInit.enzymeIndexs_DnaA_Nmer_ADP]) ...
                    )];
                dnaAPol = [
                    repInit.calculateDnaAR1234Polymerization()
                    repInit.calcuateIsDnaAR5Occupied()
                    ];
                this.log.dnaAFunctionalBoxes(:, i) = dnaAPol(:, 1);
            end
            if this.processIndex_replication
                rep = sim.processes{this.processIndex_replication};

                this.log.replisome(:, i) = [
                    rep.helicasePosition rep.leadingPosition rep.laggingPosition
                    ];
            end

            this.log.ploidy(1,i) = c.ploidy;

            if this.processIndex_ftsZPolymerization
                ftsZPol = sim.processes{this.processIndex_ftsZPolymerization};

                freeFtsZMonomers = ftsZPol.enzymes(ftsZPol.enzymeIndexs_FtsZ);
                totFtsZMonomers = ...
                    + ftsZPol.enzymes(ftsZPol.enzymeIndexs_FtsZ) ...
                    + ftsZPol.enzymes(ftsZPol.enzymeIndexs_FtsZ_GDP) ...
                    + ftsZPol.enzymes(ftsZPol.enzymeIndexs_FtsZ_activated)' * (1:9)' ...
                    + ftsZPol.boundEnzymes(ftsZPol.enzymeIndexs_FtsZ) ...
                    + ftsZPol.boundEnzymes(ftsZPol.enzymeIndexs_FtsZ_GDP) ...
                    + ftsZPol.boundEnzymes(ftsZPol.enzymeIndexs_FtsZ_activated)' * (1:9)';
                this.log.ftsZ(:, i) = [
                    freeFtsZMonomers
                    totFtsZMonomers
                    ];
            end
            this.log.ftsZRing(:, i) = [
                ftsZRing.numEdgesOneStraight
                ftsZRing.numEdgesTwoStraight
                ftsZRing.numEdgesTwoBent
                ftsZRing.numResidualBent
                ];
            this.log.pinchedDiameter(1, i) = geometry.pinchedDiameter;
        end

        function this = finalize(this, sim)
            t = sim.states{this.stateIndex_time};
            tmpi = t.values / this.stepSizeSec + 1;
            i = ceil(tmpi);

            %trim log
            fields = fieldnames(this.log);
            for j = 1:numel(fields)
                this.log.(fields{j}) = this.log.(fields{j})(:, 1:i);
            end

            %append
            this.appendHelper(i, sim);

            %print and plot
            if rem(tmpi, 1) ~= 0
                this.print(i);
            end
            this.finalizePrint();
            this.plot(i);

            %save
            this.save();
        end

        function this = save(this)
            if isempty(this.outputDirectory)
                return;
            end

            log = this.log; %#ok<PROP,NASGU>
            save([this.outputDirectory filesep 'summary.mat'], '-struct', 'log');

            if this.verbosity > 1
                try
                    saveas(this.figureHandle, [this.outputDirectory filesep 'summary.pdf']);
                catch exception
                    warning('WholeCell:warning', 'Summary figure could not be saved: %s.', exception.message);
                end
            end
        end
    end

    methods (Access = protected)
        function initializePrint(this)
            if this.verbosity <= 0
                return;
            end

            fprintf('%-5s %-3s %-4s %-10s %-89s %-29s %-11s %-11s %-3s %-7s %-9s %-41s %-6s %-7s %-11s %-10s\n', ...
                '', ...
                '', ...
                '', ...
                '', ...
                'Metabolites',...
                'RNAs', ...
                'Monomers', 'Complexes', ...
                '', '', '', ...
                'Replisome', ...
                '', ...
                '', '', '' ...
                );
            fprintf('%-5s %-3s %-4s %-10s %-89s %-29s %-11s %-11s %-3s %-7s %-9s %-41s %-6s %-7s %-11s %-10s\n', ...
                '', ...
                '', ...
                '', ...
                '', ...
                repmat('=', 1, 89),...
                repmat('=', 1, 29), ...
                repmat('=', 1, 11), repmat('=', 1, 11), ...
                '', '', '', ...
                repmat('=', 1, 41), ...
                '', ...
                '', '', '' ...
                );
            fprintf('%-5s %-3s %-4s %-10s %-8s %-8s %-8s %-8s %-9s %-8s %-9s %-8s %-15s %-4s %-4s %-4s %-4s %-4s %-4s %-5s %-5s %-5s %-5s %-3s %-7s %-9s %-13s %-13s %-13s %-6s %-7s %-11s %-10s\n',...
                'Time', ...
                'RT', ...
                'Mass', ...
                'Growth', ...
                'ATP', 'ADP', 'AMP', 'NTPs', 'Min NTP', 'AAs', 'Min AA', 'Lipids', 'Polysaccharides', ...
                'Tot', 'Nasc', 'mRNA', 'rRNA', 'sRNA', 'tRNA', ...
                'Nasct', 'Matur', 'Nasct', 'Matur', ...
                'Sup', 'DnaA', 'R1-5', ...
                'Helicase Pos', 'Leading Pos', 'Lagging Pos', ...
                'Ploidy', ...
                'FtsZ', 'FtsZ Ring', 'Pinch Diam' ...
                );
            fprintf('%-5s %-3s %-4s %-10s %-8s %-8s %-8s %-8s %-9s %-8s %-9s %-8s %-15s %-4s %-4s %-4s %-4s %-4s %-4s %-5s %-5s %-5s %-5s %-3s %-7s %-9s %-13s %-13s %-13s %-6s %-7s %-11s %-10s\n',...
                repmat('=', 1, 5), ...
                repmat('=', 1, 3), ...
                repmat('=', 1, 4), ...
                repmat('=', 1, 10), ...
                repmat('=', 1, 8), repmat('=', 1, 8), repmat('=', 1, 8), repmat('=', 1, 8), repmat('=', 1, 9), repmat('=', 1, 8), repmat('=', 1, 9), repmat('=', 1, 8), repmat('=', 1, 15), ...
                repmat('=', 1, 4), repmat('=', 1, 4), repmat('=', 1, 4), repmat('=', 1, 4), repmat('=', 1, 4), repmat('=', 1, 4), ...
                repmat('=', 1, 5), repmat('=', 1, 5), repmat('=', 1, 5), repmat('=', 1, 5), ...
                repmat('=', 1, 3), repmat('=', 1, 7), repmat('=', 1, 9), ...
                repmat('=', 1, 13), repmat('=', 1, 13), repmat('=', 1, 13), ...
                repmat('=', 1,6), ...
                repmat('=', 1, 7), repmat('=', 1, 11), repmat('=', 1, 10) ...
                );
        end

        function print(this, i)
            if this.verbosity <= 0
                return;
            end

            if ispc
                format = '%5d %3.0f %4.2f %5.3e %8d %8d %8d %8d %5d %3s %8d %5d %3s %8d %15d %4d %4d %4d %4d %4d %4d %5d %5d %5d %5d %3d %3d %3d %d %d %d %d %d %6d %6d %6d %6d %6d %6d %6.3f %3d %3d %2d %2d %2d %2d %5.3e\n';
            else
                format = '%5d %3.0f %4.2f %6.3e %8d %8d %8d %8d %5d %3s %8d %5d %3s %8d %15d %4d %4d %4d %4d %4d %4d %5d %5d %5d %5d %3d %3d %3d %d %d %d %d %d %6d %6d %6d %6d %6d %6d %6.3f %3d %3d %2d %2d %2d %2d %6.3e\n';
            end
            fprintf(format,...
                this.log.time(1, i),...
                this.log.runTime(1, i), ...
                this.log.mass(1, i), ...
                this.log.growth(1, i), ...
                this.log.metabolites(1, i),...
                this.log.metabolites(2, i),...
                this.log.metabolites(3, i),...
                this.log.metabolites(4, i),...
                this.log.metabolites(5, i),...
                this.ntpWholeCellModelIDs{this.log.metabolites(6, i)},...
                this.log.metabolites(7, i),...
                this.log.metabolites(8, i),...
                this.aaWholeCellModelIDs{this.log.metabolites(9, i)},...
                this.log.metabolites(10,i),...
                this.log.metabolites(11,i),...
                this.log.rnas(1, i),...
                this.log.rnas(6, i),...
                this.log.rnas(2, i),...
                this.log.rnas(3, i),...
                this.log.rnas(4, i),...
                this.log.rnas(5, i),...
                this.log.proteins(3, i),...
                this.log.proteins(2, i),...
                this.log.proteins(6, i),...
                this.log.proteins(5, i),...
                this.log.supercoiled(1, i), ...
                this.log.dnaA(1, i), ...
                this.log.dnaA(2, i), ...
                this.log.dnaAFunctionalBoxes(1, i), ...
                this.log.dnaAFunctionalBoxes(2, i), ...
                this.log.dnaAFunctionalBoxes(3, i), ...
                this.log.dnaAFunctionalBoxes(4, i), ...
                this.log.dnaAFunctionalBoxes(5, i), ...
                this.log.replisome(1, i), ...
                this.log.replisome(2, i), ...
                this.log.replisome(3, i), ...
                this.log.replisome(4, i), ...
                this.log.replisome(5, i), ...
                this.log.replisome(6, i), ...
                this.log.ploidy(1,i), ...
                this.log.ftsZ(1, i), ...
                this.log.ftsZ(2, i), ...
                this.log.ftsZRing(1, i), ...
                this.log.ftsZRing(2, i), ...
                this.log.ftsZRing(3, i), ...
                this.log.ftsZRing(4, i), ...
                this.log.pinchedDiameter(1, i) ...
                );
        end

        function finalizePrint(this)
            if this.verbosity <= 0
                return;
            end

            fprintf('Total simulation time: %d s\n', this.log.time(1, end));
            fprintf('Total runtime: %.3f s\n', sum(this.log.runTime));
            fprintf('\n');
        end

        function initializePlot(this)
            if this.verbosity <= 1
                return;
            end

            %create new figure
            this.figureHandle = figure();
        end

        %Plot progress of evolution of state of organism.
        function plot(this, ~)
            if this.verbosity <= 1
                return;
            end

            %run time
            axesHandle = subplot(2, 4, 1, 'Parent', this.figureHandle);
            plot(axesHandle, 0:numel(this.log.time)-1, this.log.runTime/60);
            title(axesHandle, 'Runtime')
            xlabel(axesHandle, 'Segment');
            ylabel(axesHandle, 'Run Time (m)');
            xlim(axesHandle, [0 numel(this.log.time)-1]);

            %metabolites
            axesHandle = subplot(2, 4, 2, 'Parent', this.figureHandle);
            h = plot(axesHandle, this.log.time / 3600, this.log.metabolites([1 2 3 4 5 7 8], :)');
            title(axesHandle, 'Metabolites')
            xlabel(axesHandle, 'Time (h)');
            ylabel(axesHandle, 'Metabolites');
            xlim(axesHandle, [0 this.log.time(end)] / 3600);
            legend(h, {'ATP', 'ADP', 'AMP', 'Tot NTPs', 'Min NTP', 'Tot AAs', 'Min AA'});

            %RNAs
            axesHandle = subplot(2, 4, 3, 'Parent', this.figureHandle);
            h = plot(axesHandle, this.log.time / 3600, this.log.rnas([1 6 2 3 4 5], :)');
            title(axesHandle, 'RNA')
            xlabel(axesHandle, 'Time (h)');
            ylabel(axesHandle, 'RNA');
            xlim(axesHandle, [0 this.log.time(end)] / 3600);
            legend(h, {'Total', 'Nascent', 'mRNA', 'rRNA', 'sRNA', 'tRNA'});

            %proteins
            axesHandle = subplot(2, 4, 4, 'Parent', this.figureHandle);
            h = plot(axesHandle, this.log.time/3600, this.log.proteins([1 3 2 4 6 5], :)');
            title(axesHandle, 'Protein')
            xlabel(axesHandle, 'Time (h)');
            ylabel(axesHandle, 'Protein');
            xlim(axesHandle, [0 this.log.time(end)]/3600);
            legend(h, {'Total Monomers', 'Nascent Monomers', 'Mature Monomers', 'Total Complexes', 'Nascent Complexes', 'Mature Complexes'});

            %Supercoiled
            axesHandle = subplot(2, 4, 5, 'Parent', this.figureHandle);
            plot(axesHandle, this.log.time/3600, this.log.supercoiled(1, :)');
            title(axesHandle, 'Supercoiled')
            xlabel(axesHandle, 'Time (h)');
            ylabel(axesHandle, 'No. Supercoiled Regions');
            xlim(axesHandle, [0 this.log.time(end)]/3600);

            %DnaA
            axesHandle = subplot(2, 4, 6, 'Parent', this.figureHandle);
            h = plot(axesHandle, this.log.time/3600, [this.log.dnaA/max(1, this.log.dnaA(1,1)); int16(this.log.dnaAFunctionalBoxes)]');
            title(axesHandle, 'DnaA')
            xlabel(axesHandle, 'Time (h)');
            ylabel(axesHandle, 'DnaA Count');
            xlim(axesHandle, [0 this.log.time(end)]/3600);
            legend(h, {'Free DnaA-ATP', 'Total DnaA', 'R1', 'R2', 'R3', 'R4', 'R5'});

            %Replisome
            axesHandle = subplot(2, 4, 7, 'Parent', this.figureHandle);
            h = plot(axesHandle, this.log.time/3600, this.log.replisome');
            title(axesHandle, 'Replisome')
            xlabel(axesHandle, 'Time (h)');
            ylabel(axesHandle, 'Position');
            xlim(axesHandle, [0 this.log.time(end)]/3600);
            legend(h, {'Helicase-1', 'Helicase-2', 'Leading-1', 'Leading-2', 'Lagging-1', 'Lagging-2'});

            %FtsZ
            axesHandle = subplot(2, 4, 8, 'Parent', this.figureHandle);
            h = plot(axesHandle, this.log.time/3600, [double(this.log.ftsZ/this.log.ftsZ(1, 1)); double(this.log.ftsZRing); this.log.pinchedDiameter/this.log.pinchedDiameter(1)]');
            title(axesHandle, 'FtsZ')
            xlabel(axesHandle, 'Time (h)');
            ylabel(axesHandle, 'FtsZ');
            xlim(axesHandle, [0 this.log.time(end)]/3600);
            legend(h, {'Free FtsZ-GTP', 'Total FtsZ', 'Ring-1strt', 'Ring-1bent', 'Ring-2strt', 'Ring-2 bent', 'Pinched Diameter'});

            %force display of figures
            drawnow;
        end
    end

    % Analyzes summary output of a batch of simulations. Stores analysis in mat file.
    methods (Static = true)
        function [simStats] = summarizeSimulations(baseDir)
            import edu.stanford.covert.cell.sim.util.SimulationDiskUtil;
            import edu.stanford.covert.cell.sim.util.SummaryLogger;

            simStats = SummaryLogger.getSimulationStatistics(baseDir);

            nSimulations = size(simStats, 1);

            summaryFileName = [baseDir filesep 'summary.html'];
            stateFileName = [baseDir filesep 'initialAndFinalStates.html'];
            errorFileName = [baseDir filesep 'errors.html'];

            %% calc statistics
            stateFid = fopen(stateFileName, 'w');
            errorFid = fopen(errorFileName, 'w');
            fprintf(stateFid, '<html>\n<head>\n');
            fprintf(stateFid, '<style type="text/css">\n');
            fprintf(stateFid, '  table.states {width:12000px;}\n');
            fprintf(stateFid, '  table.states thead tr th, table.states tbody tr td{width:auto;padding-left:10px;}\n');
            fprintf(stateFid, '  table.states thead tr th:first-child, table.states tbody tr td:first-child{width:auto;padding-left:0px;}\n');
            fprintf(stateFid, '  table.states thead tr:first-child th {border-top:1px solid black;}\n');
            fprintf(stateFid, '  table.states thead tr:last-child th {border-bottom:1px solid black;}\n');
            fprintf(stateFid, '  table.states tbody tr:last-child td {border-bottom:1px solid black;}\n');
            fprintf(stateFid, '  table.states tr th:first-child, table.states tr td:first-child {border-right:1px solid black}\n');
            fprintf(stateFid, '  table.states thead tr:last-child th:nth-child(%d), table.states tbody tr td:nth-child(%d) {border-right:1px solid black}\n', 1+6, 1+6);
            fprintf(stateFid, '  table.states thead tr:last-child th:nth-child(%d), table.states tbody tr td:nth-child(%d) {border-right:1px solid black}\n', 1+6+48, 1+6+48);
            fprintf(stateFid, '  table.states thead tr th:first-child, table.states tbody tr td:first-child {border-left:1px solid black}\n');
            fprintf(stateFid, '  table.states thead tr th:last-child, table.states tbody tr td:last-child {border-right:1px solid black}\n');
            fprintf(stateFid, '  table.states thead tr th {background-color: #CCCCCC;}\n');
            fprintf(stateFid, '  table.states tbody tr:nth-child(even) td {background-color: #DDDDDD;}\n');
            fprintf(stateFid, '  table.states tbody tr:nth-child(odd) td {background-color: #EEEEEE;}\n');
            fprintf(stateFid, '</style>\n');
            fprintf(stateFid, '</head>\n');
            fprintf(stateFid, '<body>\n<table class="states" cellpadding="0" cellspacing="0">\n<thead>\n%s\n</thead>', SummaryLogger.formatColNames());
            fprintf(errorFid, '<html>\n<body>\n');
            for i = 1:nSimulations
                %calc simulation status
                if ~exist(sprintf('%s/%d/state-0.mat', baseDir, i), 'file')
                elseif ~exist(sprintf('%s/%d/err.mat', baseDir, i), 'file') && ~exist(sprintf('%s/%d/summary.mat', baseDir, i), 'file')
                elseif exist(sprintf('%s/%d/err.mat', baseDir, i), 'file')
                    tmpFid = fopen(sprintf('%s/%d/err.log', baseDir, i), 'r');
                    error = reshape(fread(tmpFid, '*char'), 1, []);
                    error = strrep(error, sprintf('\n\n\n\n'), sprintf('\n'));
                    error = strrep(error, sprintf('\n\n'), sprintf('\n'));
                    fclose(tmpFid);
                    fprintf(errorFid, '<p>\n<b>Simulation #%d</b><br/>\n<pre>\n%s\n</pre>\n</p>\n', i, error);
                else
                    summary = load(sprintf('%s/%d/summary.mat', baseDir, i));
                end

                if simStats(i, SummaryLogger.SIM_STATUS_INDEX_STATUS) >= 0
                    fprintf(stateFid, '<tr>\n<td>%d</td>\n%s\n%s\n%s\n</tr>\n', i, SummaryLogger.formatStats(simStats(i, :)), SummaryLogger.formatState(summary, 1), SummaryLogger.formatState(summary, numel(summary.time)));
                else
                    fprintf(stateFid, '<tr>\n<td>%d</td>\n%s\n%s\n%s\n</tr>\n', i, SummaryLogger.formatStats(simStats(i, :)), SummaryLogger.formatState(), SummaryLogger.formatState());
                end
            end
            fprintf(stateFid, '</tbody>\n</table>\n</body>\n</html>\n');
            fprintf(errorFid, '</body>\n</html>\n');
            fclose(stateFid);
            fclose(errorFid);

            if ~any(simStats(:, SummaryLogger.SIM_STATUS_INDEX_STATUS) == -1)
                errorFid = fopen(errorFileName, 'w');
                fprintf(errorFid, '<html>\n<body>\n');
                fprintf(errorFid, 'No run-time errors were encountered.\n');
                fprintf(errorFid, '</body>\n</html>\n');
                fclose(errorFid);
            end

            fid = fopen(summaryFileName, 'w');
            fwrite(fid, SummaryLogger.getSummaryReport(simStats));
            fclose(fid);

            %% plot
            figHandle = figure();

            %initial vs. cumulative growth
            plot(simStats(:, SummaryLogger.SIM_STATUS_INDEX_INIT_GROWTH)'*3600, ...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_CUM_GROWTH)', '.');
            xlabel('Initial growth rate (cell/h)');
            ylabel('Cumulative growth rate (cell)');
            saveas(gcf, [baseDir filesep 'initialVsCumulativeGrowth.pdf']);

            %replication initiation time vs replication, cytokinesis times
            h = plot(simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME)' / 3600, [...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_TIME) ...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_CYTOKINESIS_TIME)]'/ 3600, '.');
            xlabel('Replication Initiation Time (h)');
            ylabel('Time (h)');
            legend(h, {'Replication', 'Cytokinesis'});
            saveas(gcf, [baseDir filesep 'cellCyclePhaseLengths.pdf']);

            %replication initiation duration vs replication, cytokinesis
            %durations
            h = plot(simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME)' / 3600, [...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_TIME)-simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME) ...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_CYTOKINESIS_TIME)-simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_TIME)]'/ 3600, '.');
            xlabel('Replication Initiation Time (h)');
            ylabel('Duration (h)');
            legend(h, {'Replication', 'Cytokinesis'});
            saveas(gcf, [baseDir filesep 'cellCyclePhaseDurations.pdf']);

            %initial growth rate vs replication initiation, replication,
            %cytokinesis times
            h = plot(simStats(:, SummaryLogger.SIM_STATUS_INDEX_INIT_GROWTH)'*3600, [...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME) ...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_TIME) ...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_CYTOKINESIS_TIME)]'/ 3600, '.');
            xlabel('Initial growth rate (cell/h)');
            ylabel('Time (h)');
            legend(h, {'Replication initiation', 'Replication', 'Cytokinesis'});
            saveas(gcf, [baseDir filesep 'initialGrowthVsCellCyclePhaseTimes.pdf']);

            %cumulative growth vs replication initiation, replication,
            %cytokinesis times
            h = plot(simStats(:, SummaryLogger.SIM_STATUS_INDEX_CUM_GROWTH), [...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME) ...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_TIME) simStats(:, SummaryLogger.SIM_STATUS_INDEX_CYTOKINESIS_TIME)]'/ 3600, '.');
            xlabel('Cumulative growth rate (cell)');
            ylabel('Time (h)');
            legend(h, {'Replication initiation', 'Replication', 'Cytokinesis'});
            saveas(gcf, [baseDir filesep 'cumulativeGrowthVsCellCyclePhaseTimes.pdf']);

            %initial growth rate vs replication initiation, replication,
            %cytokinesis durations
            h = plot(simStats(:, SummaryLogger.SIM_STATUS_INDEX_INIT_GROWTH)'*3600, [...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME) ....
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_TIME)-simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME) ...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_CYTOKINESIS_TIME)-simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_TIME)]'/ 3600, '.');
            xlabel('Initial growth rate (cell/h)');
            ylabel('Duration (h)');
            legend(h, {'Replication initiation', 'Replication', 'Cytokinesis'});
            saveas(gcf, [baseDir filesep 'initialGrowthVsCellCyclePhaseDurations.pdf']);

            %cumulative growth vs replication initiation, replication,
            %cytokinesis durations
            h = plot(simStats(:, SummaryLogger.SIM_STATUS_INDEX_CUM_GROWTH), [...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME) ...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_TIME)-simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME) ...
                simStats(:, SummaryLogger.SIM_STATUS_INDEX_CYTOKINESIS_TIME)-simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_TIME)]'/ 3600, '.');
            xlabel('Cumulative growth rate (cell)');
            ylabel('Duration (h)');
            legend(h, {'Replication initiation', 'Replication', 'Cytokinesis'});
            saveas(gcf, [baseDir filesep 'cumulativeGrowthVsCellCyclePhaseDurations.pdf']);

            close(figHandle);
        end

        function simStats = getSimulationStatistics(baseDir, selectedSimulations)
            import edu.stanford.covert.cell.sim.util.SimulationDiskUtil;
            import edu.stanford.covert.cell.sim.util.SummaryLogger;

            if nargin < 2
                selectedSimulations = 1:SimulationDiskUtil.getNumSimulations(baseDir);
            end

            nSimulations = numel(selectedSimulations);

            %% calc statistics
            simStats = NaN(nSimulations, 11);
            for j = 1:nSimulations
                i = selectedSimulations(j);

                %calc simulation status
                if ~exist(sprintf('%s/%d/state-0.mat', baseDir, i), 'file')
                    simStats(j, SummaryLogger.SIM_STATUS_INDEX_STATUS) = SummaryLogger.SIM_STATUS_DIDNT_START;
                elseif ~exist(sprintf('%s/%d/err.mat', baseDir, i), 'file') && ~exist(sprintf('%s/%d/summary.mat', baseDir, i), 'file')
                    simStats(j, SummaryLogger.SIM_STATUS_INDEX_STATUS) = SummaryLogger.SIM_STATUS_STILL_RUNNING;
                elseif exist(sprintf('%s/%d/err.mat', baseDir, i), 'file')
                    simStats(j, SummaryLogger.SIM_STATUS_INDEX_STATUS) = SummaryLogger.SIM_STATUS_TERMINATED_WITH_ERROR;
                else
                    summary = load(sprintf('%s/%d/summary.mat', baseDir, i), 'time', 'mass', 'growth', 'dnaAFunctionalBoxes', 'replisome', 'pinchedDiameter');
                    if summary.pinchedDiameter(end) == 0
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_STATUS) = SummaryLogger.SIM_STATUS_COMPLETED_WITH_DIVISION;
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_CYTOKINESIS_TIME) = summary.time(end);
                    else
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_STATUS) = SummaryLogger.SIM_STATUS_COMPLETED_WITHOUT_DIVISION;
                    end

                    %calc simulation statistics
                    idx = find(all(summary.dnaAFunctionalBoxes == repmat([7; 7; 7; 7; 1], 1, numel(summary.time)), 1), 1, 'first');
                    if ~isempty(idx)
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME) = summary.time(idx);
                    end

                    idx = find(any(summary.replisome, 1), 1, 'first');
                    if ~isempty(idx)
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_INIT_REP_TIME) = summary.time(idx);
                    end
                    simStats(j, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME) = min(...
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME), ...
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_INIT_REP_TIME));

                    idx = find(any(summary.replisome, 1), 1, 'last')+1;
                    if ~isempty(idx) && idx <= size(summary.time, 2)
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_REP_TIME) = summary.time(idx);
                    end

                    simStats(j, SummaryLogger.SIM_STATUS_INDEX_INIT_GROWTH) = summary.growth(1);
                    cellCycleLength = 9.0 * 3600;
                    idx = find(summary.time > cellCycleLength, 1, 'first');
                    if ~isempty(idx)
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_FIN_GROWTH) = summary.growth(idx);
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_CUM_GROWTH) = diff(summary.time(1:2)) * sum(summary.growth(1:idx));
                    else
                        f = (2 - 1) / (exp(log(2) * summary.time(end) / cellCycleLength) - 1); %#ok<CPROP,PROP>
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_FIN_GROWTH) = f * summary.growth(end);
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_CUM_GROWTH) = f * diff(summary.time(1:2)) * sum(summary.growth);
                    end

                    idx = find(summary.mass > 2 * summary.mass(1), 1, 'first');
                    if ~isempty(idx)
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_MASS_DOUBLING_TIME) = summary.time(idx-1);
                    else
                        simStats(j, SummaryLogger.SIM_STATUS_INDEX_MASS_DOUBLING_TIME) = ...
                            cellCycleLength / log(2) * log(2 / summary.mass(end)) + summary.time(end); %#ok<CPROP,PROP>
                    end

                    simStats(j, SummaryLogger.SIM_STATUS_INDEX_FIN_MASS) = summary.mass(end);
                    simStats(j, SummaryLogger.SIM_STATUS_INDEX_INIT_MASS) = summary.mass(1);
                end
            end
        end

        function value = formatColNames()
            stateColNames = {
                'Time'
                'Growth'
                'Mass'
                'ATP'
                'ADP'
                'AMP'
                'Total NTPs'
                'Min NTP'
                'Min NTP'
                'Total AAs'
                'Min AA'
                'Min AA'
                'Total Lipids'
                'Total Carbohydrates'
                'Total RNA'
                'Total mature mRNA'
                'Total mature rRNA'
                'Total mature sRNA'
                'Total mature tRNA'
                'Total immature RNA'
                'Total Monomers'
                'Total Mature Monomers'
                'Total Immature Monomers'
                'Total Complexs'
                'Total Mature Complexs'
                'Total Immature Complexs'
                'Supercoiled'
                'Free'
                'Total'
                'R1'
                'R2'
                'R3'
                'R4'
                'R5'
                'Helicase Position - 1'
                'Helicase Position - 2'
                'Leading Polymerase Position - 1'
                'Leading Polymerase Position - 2'
                'Lagging Polymerase Position - 2'
                'Lagging Polymerase Position - 2'
                'Ploidy'
                'Free'
                'Total'
                'One straight'
                'One bent'
                'Two straight'
                'Residual'
                'Pinched Diameter'
                };
            colNames = [{
                'Simulation'
                'Status'
                'Replication initiation time'
                'Initiation of replication time'
                'Replication time'
                'Cytokinesis time'
                'Cumulative growth'
                };
                stateColNames; stateColNames];

            value = [];

            value = [value sprintf('<tr>\n')];
            value = [value sprintf('<th> </th>\n')];
            value = [value sprintf('<th colspan="6">%s</th>\n', 'Summary')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', numel(stateColNames), 'Initial State')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', numel(stateColNames), 'Final State')];
            value = [value sprintf('</tr>\n')];

            value = [value sprintf('<tr>\n')];
            value = [value sprintf('<th> </th>\n')];
            value = [value sprintf('<th colspan="6">%s</th>\n', ' ')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %Time
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %Growth
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %Mass
            value = [value sprintf('<th colspan="%d">%s</th>\n', 11, 'Metabolite')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 6, 'RNA')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 6, 'Protein')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %Supercoiled
            value = [value sprintf('<th colspan="%d">%s</th>\n', 2, 'DnaA')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 5, 'Functional DnaA Boxes')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 6, 'Replisome')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %ploidy
            value = [value sprintf('<th colspan="%d">%s</th>\n', 2, 'FtsZ')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 4, 'FtsZ Ring')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %Pinched Diameter
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %Time
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %Growth
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %Mass
            value = [value sprintf('<th colspan="%d">%s</th>\n', 11, 'Metabolite')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 6, 'RNA')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 6, 'Protein')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %Supercoiled
            value = [value sprintf('<th colspan="%d">%s</th>\n', 2, 'DnaA')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 5, 'Functional DnaA Boxes')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 6, 'Replisome')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %ploidy
            value = [value sprintf('<th colspan="%d">%s</th>\n', 2, 'FtsZ')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 4, 'FtsZ Ring')];
            value = [value sprintf('<th colspan="%d">%s</th>\n', 1, ' ')]; %Pinched Diameter
            value = [value sprintf('</tr>\n')];

            value = [value sprintf('<tr>\n')];
            for i = 1:numel(colNames)
                value = [value sprintf('<th>%s</th>\n', colNames{i})]; %#ok<AGROW>
            end
            value = [value sprintf('</tr>\n')];
        end

        function value = formatStats(stats)
            import edu.stanford.covert.cell.sim.util.SummaryLogger;

            switch stats(SummaryLogger.SIM_STATUS_INDEX_STATUS)
                case -3, status = 'Not yet started';
                case -2, status = 'Not yet terminated';
                case -1, status = 'Terminated with error';
                case 0, status = 'Terminated with division';
                case 1, status = 'Terminated without division';
            end
            value = sprintf('<td>%s</td>\n', status);

            if ~isnan(stats(SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME))
                value = [value sprintf('<td>%d</td>\n', stats(SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME))];
            else
                value = [value sprintf('<td> </td>\n')];
            end

            if ~isnan(stats(SummaryLogger.SIM_STATUS_INDEX_INIT_REP_TIME))
                value = [value sprintf('<td>%d</td>\n', stats(SummaryLogger.SIM_STATUS_INDEX_INIT_REP_TIME))];
            else
                value = [value sprintf('<td> </td>\n')];
            end

            if ~isnan(stats(SummaryLogger.SIM_STATUS_INDEX_REP_TIME))
                value = [value sprintf('<td>%d</td>\n', stats(SummaryLogger.SIM_STATUS_INDEX_REP_TIME))];
            else
                value = [value sprintf('<td> </td>\n')];
            end

            if ~isnan(stats(SummaryLogger.SIM_STATUS_INDEX_CYTOKINESIS_TIME))
                value = [value sprintf('<td>%d</td>\n', stats(SummaryLogger.SIM_STATUS_INDEX_CYTOKINESIS_TIME))];
            else
                value = [value sprintf('<td> </td>\n')];
            end

            if ~isnan(stats(SummaryLogger.SIM_STATUS_INDEX_CUM_GROWTH))
                value = [value sprintf('<td>%e</td>\n', stats(SummaryLogger.SIM_STATUS_INDEX_CUM_GROWTH))];
            else
                value = [value sprintf('<td> </td>\n')];
            end
        end

        function value = formatState(summary, idx)
            fields = {
                'time' 1
                'growth' 1
                'mass' 1
                'metabolites' 11
                'rnas' 6
                'proteins' 6
                'supercoiled' 1
                'dnaA' 2
                'dnaAFunctionalBoxes' 5
                'replisome' 6
                'ploidy' 1
                'ftsZ' 2
                'ftsZRing' 4
                'pinchedDiameter' 1
                };
            value = [];
            for i = 1:size(fields, 1)
                for j = 1:fields{i, 2}
                    if nargin == 0 || ~isfield(summary, fields{i, 1}) || j > size(summary.(fields{i, 1}), 1)
                        value = [value sprintf('<td> </td>\n')]; %#ok<AGROW>
                    elseif summary.(fields{i, 1})(j, idx) == ceil(summary.(fields{i, 1})(j, idx))
                        value = [value sprintf('<td>%d</td>\n', summary.(fields{i, 1})(j, idx))]; %#ok<AGROW>
                    else
                        value = [value sprintf('<td>%e</td>\n', summary.(fields{i, 1})(j, idx))]; %#ok<AGROW>
                    end
                end
            end
        end

        %% build summary report
        function summary = getSummaryReport(simStats)
            import edu.stanford.covert.cell.sim.util.SummaryLogger;

            nSimulations = size(simStats, 1);
            nTerminatedWithDivision = sum(simStats(:, SummaryLogger.SIM_STATUS_INDEX_STATUS) == 0);
            nTerminatedWithoutDivision = sum(simStats(:, SummaryLogger.SIM_STATUS_INDEX_STATUS) == 1);
            nTerminatedWithError = sum(simStats(:, SummaryLogger.SIM_STATUS_INDEX_STATUS) == -1);
            nNotYetTerminated = sum(simStats(:, SummaryLogger.SIM_STATUS_INDEX_STATUS) == -2);
            nNotYetStarted = sum(simStats(:, SummaryLogger.SIM_STATUS_INDEX_STATUS) == -3);
            repInitTimes = simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_INIT_TIME) / 3600;
            initRepTimes = simStats(:, SummaryLogger.SIM_STATUS_INDEX_INIT_REP_TIME) / 3600;
            repTimes = simStats(:, SummaryLogger.SIM_STATUS_INDEX_REP_TIME) / 3600;
            cytTimes = simStats(:, SummaryLogger.SIM_STATUS_INDEX_CYTOKINESIS_TIME) / 3600;
            initGrowths = simStats(:, SummaryLogger.SIM_STATUS_INDEX_INIT_GROWTH) * 3600;
            finGrowths = simStats(:, SummaryLogger.SIM_STATUS_INDEX_FIN_GROWTH) * 3600;
            cumGrowths = simStats(:, SummaryLogger.SIM_STATUS_INDEX_CUM_GROWTH);
            initMass = simStats(:, SummaryLogger.SIM_STATUS_INDEX_INIT_MASS);
            finMass = simStats(:, SummaryLogger.SIM_STATUS_INDEX_FIN_MASS);

            %styles
            summary = [];
            summary = [summary sprintf('<html>\n')];
            summary = [summary sprintf('<head>\n')];
            summary = [summary sprintf('<style type="text/css">\n')];
            summary = [summary sprintf('  table.summary tr th {text-align:left; width:400px;}\n')];
            summary = [summary sprintf('  table.summary tr td {text-align:right; padding-left:10px}\n')];
            summary = [summary sprintf('</style>\n')];
            summary = [summary sprintf('</head>\n')];
            summary = [summary sprintf('<body>\n')];

            %status
            summary = [summary sprintf('<table class="summary" cellpadding="0" cellspacing="0">\n')];
            summary = [summary sprintf('<tr><th>%s</th><td>%d</td><td>%5.1f%%</td></tr>\n', 'No. simulations', nSimulations, 100)];
            summary = [summary sprintf('<tr><th>%s</th><td>%d</td><td>%5.1f%%</td></tr>\n', 'Terminated with division', nTerminatedWithDivision, nTerminatedWithDivision/nSimulations*100)];
            summary = [summary sprintf('<tr><th>%s</th><td>%d</td><td>%5.1f%%</td></tr>\n', 'Terminated without division',nTerminatedWithoutDivision, nTerminatedWithoutDivision/nSimulations*100)];
            summary = [summary sprintf('<tr><th>%s</th><td>%d</td><td>%5.1f%%</td></tr>\n', 'Terminated with error', nTerminatedWithError, nTerminatedWithError/nSimulations*100)];
            summary = [summary sprintf('<tr><th>%s</th><td>%d</td><td>%5.1f%%</td></tr>\n', 'Not yet terminated', nNotYetTerminated, nNotYetTerminated/nSimulations*100)];
            summary = [summary sprintf('<tr><th>%s</th><td>%d</td><td>%5.1f%%</td></tr>\n', 'Not yet started', nNotYetStarted, nNotYetStarted/nSimulations*100)];
            summary = [summary sprintf('</table>\n<br/>\n')];

            %times
            meanMassDoublingTime = 9.0 / nanmean(cumGrowths);
            stdMassDoublingTime = sqrt(9.0 / nanmean(cumGrowths)) * nanstd(cumGrowths);
            summary = [summary sprintf('<table class="summary" cellpadding="0" cellspacing="0">\n')];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.1f ± %5.1f</td></tr>\n', 'Mass doubling time (h)', meanMassDoublingTime, stdMassDoublingTime)];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.1f ± %5.1f</td></tr>\n', 'Cell cycle length (h)', nanmean(cytTimes), nanstd(cytTimes))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.1f ± %5.1f</td></tr>\n', 'Replication initiation time (h)', nanmean(repInitTimes), nanstd(repInitTimes))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.1f ± %5.1f</td></tr>\n', 'Initiation of replication time (h)', nanmean(initRepTimes), nanstd(initRepTimes))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.1f ± %5.1f</td></tr>\n', 'Replication time (h)', nanmean(repTimes), nanstd(repTimes))];
            summary = [summary sprintf('</table>\n<br/>\n')];

            %durations
            summary = [summary sprintf('<table class="summary" cellpadding="0" cellspacing="0">\n')];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.1f ± %5.1f</td></tr>\n', 'Replication initiation duration (h)', ...
                nanmean(repInitTimes), nanstd(repInitTimes))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.1f ± %5.1f</td></tr>\n', 'Replication initiation-replisome formation duration (h)', ...
                nanmean(initRepTimes - repInitTimes), nanstd(initRepTimes - repInitTimes))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.1f ± %5.1f</td></tr>\n', 'Replication duration (h)', ...
                nanmean(repTimes - initRepTimes), nanstd(repTimes - initRepTimes))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.1f ± %5.1f</td></tr>\n', 'Cytokinesis duration (h)', ...
                nanmean(cytTimes - repTimes), nanstd(cytTimes - repTimes))];
            summary = [summary sprintf('</table>\n<br/>\n')];

            %growth
            summary = [summary sprintf('<table class="summary" cellpadding="0" cellspacing="0">\n')];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.3f ± %5.3f</td></tr>\n', 'Growth at t==0 (1/cells/h)', ...
                nanmean(initGrowths), nanstd(initGrowths))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.3f ± %5.3f</td></tr>\n', 'Growth at t==τ (1/cells/h)', ...
                nanmean(finGrowths), nanstd(finGrowths))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.3f ± %5.3f</td></tr>\n', 'Cumulative Growth over τ (1/cells)', ...
                nanmean(cumGrowths), nanstd(cumGrowths))];
            summary = [summary sprintf('</table>\n<br/>\n')];

            %mass
            summary = [summary sprintf('<table class="summary" cellpadding="0" cellspacing="0">\n')];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.3f ± %5.3f</td></tr>\n', 'Mean Mass at t==0 (cells)', ...
                nanmean(initMass), nanstd(initMass))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.3f</td></tr>\n', 'Median Mass at t==0; (cells)', ...
                nanmedian(initMass))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.3f ± %5.3f</td></tr>\n', 'Mass Mass at t==τ (cells)', ...
                nanmean(finMass), nanstd(finMass))];
            summary = [summary sprintf('<tr><th>%s</th><td>%5.3f</td></tr>\n', 'Median at t==τ (cells)', ...
                nanmedian(finMass))];
            summary = [summary sprintf('</table>\n')];

            summary = [summary sprintf('</body>\n')];
            summary = [summary sprintf('</html>\n')];
        end
    end
end


function r = isempty(s)

%tstoolbox/@core/isempty
%   Syntax:
%     * r = isempty(s)
%
%   Input Arguments:
%     * s - core object
%
%   test if core contains no (valid) data
%
% Copyright 1997-2001 DPI Goettingen, License http://www.physik3.gwdg.de/tstool/gpl.txt

r = isempty(s.data);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   Simulation_close_tool:
%   Set the tool_open variable to zero. When drawn using the drawrobot3d function
%   the tool will be drawn with the coordinates defined as open.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

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

global configuration robot

robot.tool.tool_open=0;

drawrobot3d(robot, robot.q);


function [index, sequence] = sequence_find(sequences, name)
% sequence_find Find a sequence by its name
%
% Find a sequence by its name in a cell array of sequence structures. Returns index of sequence and its structure.
% If a sequence is not found the function returns an empty matrix.
%
% Input:
% - sequences: Cell array of sequence structures.
% - name: A string containing sequence namer.
%
% Output:
% - index: Index of the sequence in the cell array or empty matrix if not found.
% - sequence: The sequence structure.

index = find(cellfun(@(t) strcmp(t.name, name), sequences, 'UniformOutput', true), 1);

if isempty(index)
    sequence = [];
else
    sequence = sequences{index};
end;


function opt = mpoption_info_ipopt(selector)
%MPOPTION_INFO_IPOPT  Returns MATPOWER option info for IPOPT.
%
%   DEFAULT_OPTS = MPOPTION_INFO_IPOPT('D')
%   VALID_OPTS   = MPOPTION_INFO_IPOPT('V')
%   EXCEPTIONS   = MPOPTION_INFO_IPOPT('E')
%
%   Returns a structure for IPOPT options for MATPOWER containing ...
%   (1) default options,
%   (2) valid options, or
%   (3) NESTED_STRUCT_COPY exceptions for setting options
%   ... depending on the value of the input argument.
%
%   This function is used by MPOPTION to set default options, check validity
%   of option names or modify option setting/copying behavior for this
%   subset of optional MATPOWER options.
%
%   See also MPOPTION.

%   MATPOWER
%   Copyright (c) 2014-2016, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

if nargin < 1
    selector = 'D';
end
if have_feature('ipopt')
    switch upper(selector)
        case {'D', 'V'}     %% default and valid options
            opt = struct(...
                'ipopt',        struct(...
                    'opts',         [], ...
                    'opt_fname',    '', ...
                    'opt',          0 ...
                ) ...
            );
        case 'E'            %% exceptions used by nested_struct_copy() for applying
            opt = struct(...
                'name',         { 'ipopt.opts' }, ...
                'check',        0 ...
                );
%                 'copy_mode',    { @ipopt_options } ...
        otherwise
            error('mpoption_info_ipopt: ''%s'' is not a valid input argument', selector);
    end
else
    opt = struct([]);       %% IPOPT is not available
end


function D = addcroplabel(D, words)
%
% Adds the field object.crop (it also considers an occluded object as a
% cropped object).
%
% Removes the words 'crop', 'occluded', etc. from the object name.

if nargin < 2
    words = {'crop', 'occluded', 'part'};
end

Nwords = length(words);

for i = 1:length(D)
    if isfield(D(i).annotation, 'object')
        Nobjects = length(D(i).annotation.object);
        for j = 1:Nobjects
            if isfield(D(i).annotation.object(j), 'occluded')
                if strcmp(D(i).annotation.object(j).occluded,'yes')
                    D(i).annotation.object(j).crop = '1';
                end
            end

            w = lower(getwords(D(i).annotation.object(j).name));
            k = ismember(w, words);

            if sum(k)>0
                D(i).annotation.object(j).crop = '1';
                % remove words
                for n = 1:Nwords
                    D(i).annotation.object(j).name = strrep(D(i).annotation.object(j).name, words{n}, '');
                end
            else
                D(i).annotation.object(j).crop = '0';
            end
        end
    end
end

% Make sure there are no new empty object names
D = LMvalidobjects(D);


function handles = initalizeFields(handles)
%INITIALIZEFIELDS    Initialize fonts when CHEBGUI is started.
%
%  Calling sequence:
%       HANDLES = INITIALIZEFIELDS(HANDLES)
%  where
%       HANDLES:    A Matlab handle of the CHEBGUI figure.
%

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

% Set the default font and font size used:
myFont = 'Monospaced';
mySize = 12;

% Obtain a list of all GUI elements.
names = fieldnames(handles);
% Find what elements are input and button elements:
inputLocs = strfind(names, 'input_');
buttonLocs = strfind(names, 'button_');
% Also want the same font for the iter_list information box.
iterListLoc = strfind(names, 'iter_list');
% And what fields are text fields:
textLocs = strfind(names, 'text_');
% Combine all the locations of elements whose font we wish to change
monospacedLocs = strcat(inputLocs, iterListLoc);
variableWidthLocs = strcat(buttonLocs, textLocs);

% Loop through the elements we want to specify the font of.
for fieldCounter = 1:length(inputLocs)
    if ( ~isempty(monospacedLocs{fieldCounter}) )
        % Access the field values dynamically using the .( ) call.
        set(handles.(names{fieldCounter}), 'FontName', myFont);
        set(handles.(names{fieldCounter}), 'FontSize', mySize + 1);
    end

    if ( ~isempty(variableWidthLocs{fieldCounter}) )
        set(handles.(names{fieldCounter}), 'FontSize', mySize);
    end
end

% Set the string for popup-menu for the choice of plots:
set(handles.popupmenu_bottomFig,'String', ...
    {'Convergence of Newton iteration', ...
    'Coefficients of the solution'}, ...
    'fontsize', handles.fontsizePanels);

end


classdef TestLSDDetector
    %TestLSDDetector

    properties (Constant)
        im = fullfile(mexopencv.root(),'test','tsukuba_l.png');
        kfields = {'angle', 'class_id', 'octave', 'pt', 'response', ...
            'size', 'startPoint', 'endPoint', 'startPointInOctave', ...
            'endPointInOctave', 'lineLength', 'numOfPixels'};
    end

    methods (Static)
        function test_detect_img
            obj = cv.LSDDetector();
            img = imread(TestLSDDetector.im);

            klines = obj.detect(img, 'Scale',2, 'NumOctaves',1);
            validateattributes(klines, {'struct'}, {'vector'});
            assert(all(ismember(TestLSDDetector.kfields, fieldnames(klines))));
        end

        function test_detect_imgset
            img = imread(TestLSDDetector.im);
            imgs = {img, img};
            obj = cv.LSDDetector();

            klines = obj.detect(imgs, 'Scale',2, 'NumOctaves',1);
            validateattributes(klines, {'cell'}, ...
                {'vector', 'numel',numel(imgs)});
            cellfun(@(kpt) validateattributes(kpt, {'struct'}, ...
                {'vector'}), klines);
            cellfun(@(kpt) assert(all(ismember(TestLSDDetector.kfields, ...
                fieldnames(kpt)))), klines);
        end

        function test_detect_mask
            img = imread(TestLSDDetector.im);
            mask = zeros(size(img), 'uint8');
            mask(:,1:end/2) = 255;  % only search left half of the image
            obj = cv.LSDDetector();

            klines = obj.detect(img, 'Mask',mask);
            validateattributes(klines, {'struct'}, {'vector'});
            assert(all(ismember(TestLSDDetector.kfields, fieldnames(klines))));
        end
    end

end


function sts = match(item, spec)

% function sts = match(item, spec)
% This function is an implementation of find to search the cfg tree for
% certain entries.
%
% sts = match(item, spec)
% Spec must be a cell array of struct arrays with one or more fields. Each
% struct must contain two fields - 'name' and 'value'.
% An item matches, if it has a field with the specified field name and the
% contents of this field equals the contents of spec.value. If the field
% name is 'class', an item matches, if its class name is equal to
% spec.value.
% Matches within each struct array are OR-concatenated, while matches
% between struct arrays are AND-concatenated.
% An empty spec always matches.
%
% This code is part of a batch job configuration system for MATLAB. See
%      help matlabbatch
% for a general overview.
%_______________________________________________________________________
% Copyright (C) 2007 Freiburg Brain Imaging

% Volkmar Glauche
% $Id: match.m 2919 2009-03-23 13:45:41Z volkmar $

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

% match an empty spec
sts = true;

for k = 1:numel(spec)
    % Assume no match
    sts = false;
    for l = 1:numel(spec{k})
        if strcmp(spec{k}(l).name, 'class')
            sts = strcmp(spec{k}(l).value, class(item));
        elseif any(strcmp(spec{k}(l).name, mysubs_fields))
            sts = isequal(spec{k}(l).value, ...
                          subsref(item, substruct('.', spec{k}(l).name)));
        end;
        if sts
            % OR: success on first match
            break;
        end;
    end;
    if ~sts
        % AND: fail on first non-match
        break;
    end;
end;


%  Save NIFTI or ANALYZE dataset that is loaded by "load_untouch_nii.m".
%  The output image format and file extension will be the same as the
%  input one (NIFTI.nii, NIFTI.img or ANALYZE.img). Therefore, any file
%  extension that you specified will be ignored.
%
%  Usage: save_untouch_nii(nii, filename)
%
%  nii - nii structure that is loaded by "load_untouch_nii.m"
%
%  filename  - 	NIFTI or ANALYZE file name.
%
%  - Jimmy Shen (jimmy@rotman-baycrest.on.ca)
%
function save_untouch_nii(nii, filename)

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

      error('Usage: save_untouch_nii(nii, filename)');
   end

   if ~isfield(nii,'untouch') | nii.untouch == 0
      error('Usage: please use ''save_nii.m'' for the modified structure.');
   end

   if isfield(nii.hdr.hist,'magic') & strcmp(nii.hdr.hist.magic(1:3),'ni1')
      filetype = 1;
   elseif isfield(nii.hdr.hist,'magic') & strcmp(nii.hdr.hist.magic(1:3),'n+1')
      filetype = 2;
   else
      filetype = 0;
   end

   [p,f] = fileparts(filename);
   fileprefix = fullfile(p, f);

   write_nii(nii, filetype, fileprefix);

%   %  So earlier versions of SPM can also open it with correct originator
 %  %
  % if filetype == 0
   %   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, 'M');
%   elseif filetype == 1
 %     M=[];
  %    save(fileprefix, 'M');
   %end

   return					% save_untouch_nii

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

   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 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_untouch_nii_hdr(hdr, fid);

      if ~isempty(ext)
         save_nii_ext(ext, fid);
      end
   elseif filetype == 1
      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_untouch_nii_hdr(hdr, fid);

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

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

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

      save_untouch0_nii_hdr(hdr, fid);

      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

      nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]);
   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