function profreport(varargin)
%PROFREPORT Profiler Report Generator.
%   PROFREPORT(BASENAME,INFO) generates a report in HTML
%   format that summarizes MATLAB function call statistics
%   logged by the MATLAB Profiler.  PROFREPORT then displays
%   the report in your web browser.  BASENAME is the base
%   filename used to save the report.  Because the HTML report
%   consists of several different files, BASENAME should not
%   have an extension.  INFO is the Profiler information
%   structure returned by the command INFO = PROFILE('info').
%
%   PROFREPORT(BASENAME) generates the report using the
%   current MATLAB Profiler statistics.
%
%   PROFREPORT(INFO) generates the report from the specified
%   INFO structure using a temporary filename.  The temporary
%   filename is generated using TEMPNAME.
%
%   PROFREPORT generates the report using the current MATLAB
%   Profiler statistics and using a temporary filename.
%
%   Calling PROFREPORT will suspend the profiler.
%
%   See also PROFILE.

%   PROFREPORT(H,BASENAME) generates the report using the
%   profiler statistics associated with the handle H.
%
%   PROFREPORT(H) generates the report using the profiler
%   statistics associated with the handle H and using a
%   temporary filename.

%   Steven L. Eddins
%   Copyright 1984-2000 The MathWorks, Inc. 
%   $Revision: 1.23 $  $Date: 2000/08/17 19:56:44 $

[fname,structArray,history,clockPrec,pname,msg] = ParseInputs(varargin{:});
if (~isempty(msg))
    error(msg);
end

% Preprocess the function structure array.
if(strcmp(pname, 'MATLAB'))
  structArray = MungeNames(structArray);
end
structArray = ComputeTimes(structArray);

[path,name,ext,ver] = fileparts(fname);
if (isempty(path))
    path = pwd;
end
frameName = [name '.html'];
contentsName = [name '_contents' '.html'];
summaryName = [name '_summary' '.html'];
detailsName = [name '_details' '.html'];
helpName = [name '_help' '.html'];
if (~isempty(history))
    if ((size(history,2)/2) > 500)
        qstr = sprintf('Because there are %d recorded function calls, generating the history report may take a long time.  Do you wish to generate the history report anyway?', floor(size(history,2)/2));
        result = questdlg(qstr);
        if (strcmp(result, 'Yes'))
            historyName = [name '_history' '.html'];
            
        elseif (strcmp(result, 'No'))
            historyName = '';
            
        elseif (strcmp(result, 'Cancel'))
            return;
        end
    else
        historyName = [name '_history' '.html'];
    end
else
    historyName = '';
end
    
PrintHTMLFrames(path, frameName, contentsName, summaryName, pname);
PrintHTMLContentsFrame(path, contentsName, summaryName, ...
                detailsName, historyName, helpName, structArray, pname);
PrintHTMLSummaryFrame(path, summaryName, detailsName, ...
                structArray, clockPrec, pname);
PrintHTMLFunctionsFrame(path, detailsName, structArray, clockPrec, pname);
if (~isempty(historyName))
    PrintHTMLHistoryFrame(path, historyName, detailsName, ...
                    history, structArray, pname);
end
if strcmp(pname, 'Simulink')
  PrintHTMLHelpFrame(path, helpName, pname);
end
  
htmlFile = fullfile(path,frameName);

showUsingOldWeb = 1;  % assume
if strcmp(pname,'Simulink')
  try
    hBrowser = javaMethod('getHelpBrowser', ...
                           'com.mathworks.ide.help.HelpBrowser');
  catch
    hBrowser = [];
  end
  if ~isempty(hBrowser)
    % The last argument to setCurrentLocation determines whether or not
    % the Help Navigator is hidden if the Help Browser is brought up for
    % the first time.  A "1" means it should be hidden.
    setCurrentLocation(hBrowser , 'Simulink Profiler', htmlFile, 1);
    showUsingOldWeb = 0;
    status = 0;
  else
    disp(['Unable to open profile report in the MATLAB Help Browser. ' ...
         'Hyper-links to Simulink models will only work in the MATLAB ' ...
         'Help Browser.']);
  end
end

if showUsingOldWeb
  status = web(htmlFile,'-browser');
end

switch status
case 0
    % no action required
case 1
    warning('Report saved, but could not find web browser');
case 2
    warning('Report saved, but could not launch web browser');
otherwise
    % unknown status code from web
end

%%%
%%% PrintHTMLFrames
%%%
function PrintHTMLFrames(path, frameName, contentsName, summaryName, pname)
% Prints the top-level HTML document, which contains the frameset tags.

fid = fopen(fullfile(path,frameName),'wt');
if (fid < 0)
    error('Error opening report file for writing');
end
fprintf(fid, '<html> \n');

fprintf(fid, '<script language="JavaScript">\n');
fprintf(fid, '        setTimeout ("forceToTop()", 0);\n');
fprintf(fid, '        function forceToTop() {\n');
fprintf(fid, '                if (self != top) {\n');
fprintf(fid, '                     top.location = document.location;\n');
fprintf(fid, '                }\n');
fprintf(fid, '        }\n');
fprintf(fid, '</script>\n');

fprintf(fid, '<head> \n');
fprintf(fid, '<title>%s Profiler Report</title>\n', pname);
fprintf(fid, '</head>\n');

fprintf(fid, '<frameset rows="50,*">\n');
fprintf(fid, '   <frame name="Frame 1" src="%s" scrolling="auto"> \n', ...
                contentsName);
fprintf(fid, '   <frame name="Frame 2" src="%s" scrolling="auto"> \n', ...
                summaryName);

fprintf(fid, '</frameset> \n');

fprintf(fid, '<body> \n');
fprintf(fid, '</body> \n');
fprintf(fid, '</html> \n');
fclose(fid);

%%%
%%% PrintHTMLContentsFrame
%%%
function PrintHTMLContentsFrame(path, contentsName, summaryName, ...
                                detailsName, historyName, helpName, ...
                                array, pname)
% Prints the navigation frame.

fid = fopen(fullfile(path,contentsName),'wt');
if (fid < 0)
    error('Error opening report file for writing');
end
fprintf(fid, '<html> \n');
fprintf(fid, '<head> \n');
fprintf(fid, '</head>\n');

fprintf(fid, '<body bgcolor="#FFF7DE" link="#247B2B" vlink="#247B2B" > \n\n');
fprintf(fid, '<a href="%s" target="Frame 2">Summary</a>\n', summaryName);
fprintf(fid, '&nbsp;|&nbsp; <a href="%s" target="Frame 2">Function Details</a>\n', ...
                detailsName);
if (~isempty(historyName))
    fprintf(fid, '&nbsp;|&nbsp; <a href="%s" target="Frame 2">Function Call History</a>\n', ...
                    historyName);
end
if  strcmp(pname,'Simulink')
  fprintf(fid, '&nbsp;|&nbsp; <a href="%s" target="Frame 2">Simulink Profiler Help</a>\n', ...
          helpName);
  fprintf(fid, ['&nbsp;|&nbsp; <a href="matlab: slprofile_unhilite_system' ...
                ' %s"> Clear Hilited Blocks </a>\n'], array(1).FileName);
end
fprintf(fid, '</body>\n');
fprintf(fid, '</html> \n');
fclose(fid);

%%%
%%% PrintHTMLSummaryFrame
%%%
function PrintHTMLSummaryFrame(path, summaryName, detailsName, structArray, clockPrec, pname)
% Prints the summary frame

fid = fopen(fullfile(path, summaryName), 'wt');
if (fid < 0)
    error('Could not open report file for writing.');
end

fprintf(fid, '<html>\n');
fprintf(fid, '<head>\n');
fprintf(fid, '</head>\n');
fprintf(fid, '<body bgcolor="#FFF7DE" link="#247B2B" vlink="#247B2B" > \n\n');
fprintf(fid, '<h1>%s Profile Report:  Summary</h1>\n', pname);

fprintf(fid, '<p><em> Report generated %s </em></p>\n', datestr(now));

totalTime = sum([structArray.SelfTime]);
[counts,typenames] = CountFuncs(structArray);

fprintf(fid, '<table>\n');
fprintf(fid, '<tr> <td> Total recorded time: </td>\n');
fprintf(fid, '<td align="right"> %6.2f&nbsp;s </td> </tr>\n', totalTime);

for k = 1:length(counts)
  if(counts(k) > 0)
    fprintf(fid, '<tr> <td> Number of %ss: </td>\n', typenames{k});
    fprintf(fid, '<td align="right"> %d </td> </tr>\n', counts(k));
  end
end

fprintf(fid, '<tr> <td> Clock precision: </td>\n');
fprintf(fid, '<td align="right"> %5.3f&nbsp;s </td> </tr>\n', clockPrec);
fprintf(fid, '</table>\n\n');

PrintHTMLFunctionList(pname, fid, detailsName, structArray, clockPrec);


fprintf(fid, '</body>\n');
fprintf(fid, '</html>\n');

fclose(fid);

%%%
%%% PrintHTMLHistoryFrame
%%%
function PrintHTMLHistoryFrame(path, historyName, detailsName, history, structArray, pname)
% Prints the history frame

fid = fopen(fullfile(path, historyName), 'wt');
if (fid < 0)
    error('Could not open report file for writing.');
end

wbHandle = waitbar(0,'Generating function call history profile report ...');

fprintf(fid, '<html>\n');
fprintf(fid, '<head>\n');
fprintf(fid, '</head>\n');
fprintf(fid, '<body bgcolor="#FFF7DE" link="#247B2B" vlink="#247B2B" > \n\n');
fprintf(fid, '<h1>%s Profile Report:  Function Call History</h1>\n', pname);

stackDepth = -1;
for k = 1:size(history,2)
    if (history(1,k) == 0)
        % Function is called
        stackDepth = stackDepth + 1;
        index = history(2,k);
        href = sprintf('%s#Fcn_%d', detailsName, index);
        spaces = ['<code>' repmat('&nbsp;&nbsp;',[1 stackDepth*2]) '</code>'];
        fprintf(fid, '<nobr>%s%s', spaces, ...
                        Link(FormatName(structArray(index).FunctionName),href));
        if (isempty(structArray(index).FileName))
            fprintf(fid, '</nobr>');
        else
            fprintf(fid, '&nbsp;&nbsp;&nbsp;&nbsp;(%s)</nobr>', ...
                            FormatPath(pname,structArray(index).FileName));
        end
        fprintf(fid, '<br><br>\n');
        
    else
        stackDepth = stackDepth - 1;
    end
    
    if (mod(k,10) == 0)
        waitbar(k/size(history,2));
    end
end
    
fprintf(fid, '</body>\n');
fprintf(fid, '</html>\n');

fclose(fid);

close(wbHandle);


%%%
%%% PrintHTMLHelpFrame
%%%
function PrintHTMLHelpFrame(path, helpName, pname)
% Prints the simulink help frame

fid = fopen(fullfile(path, helpName), 'wt');
if (fid < 0)
    error('Could not open report file for writing.');
end

fprintf(fid, '<html>\n');
fprintf(fid, '<head>\n');
fprintf(fid, '</head>\n');
fprintf(fid, '<body bgcolor="#FFF7DE" link="#247B2B" vlink="#247B2B" > \n\n');
fprintf(fid, '<h1>%s Profile Report:  Simulink Profiler Help</h1>\n', pname);

fprintf(fid, '<h2> Summary Legend: </h2>\n');
fprintf(fid, '<dl> \n');
fprintf(fid, ['<dt> Total recorded time: <dd> Total time for simulating' ...
              ' the model.\n']);
fprintf(fid, ['<dt> Number of Block Methods: <dd> Total number of' ...
              ' methods called by individual blocks in the model.\n']);
fprintf(fid, ['<dt> Number of Internal Methods: <dd> Total number' ...
              ' of internal Simulink methods called by the model.\n']);
fprintf(fid, ['<dt> Number of Nonvirtual Subsystem Methods: <dd>' ...
              ' Number of methods called by the model and any' ...
               ' nonvirtual subystems in the model.\n']);
fprintf(fid, ['<dt> Clock precision: <dd> Precision of the profiler\''s' ...
              ' time measurement.\n']);
fprintf(fid, '</dl>\n');
fprintf(fid, '<h2> Function List Legend: </h2>\n');
fprintf(fid, '<dl>\n');
fprintf(fid, ['<dt> Time: <dd> Time spent in this function, including' ...
              ' all child functions called.\n']);
fprintf(fid, ['<dt> Calls: <dd> Number of times this function was' ...
              ' called.\n']);
fprintf(fid, '<dt> Time/call: <dd> Time spent per call.\n');
fprintf(fid, ['<dt> Self time: <dd> Total time spent in this function, ' ...
              'not including any calls to child functions.\n']);
fprintf(fid, ['<dt> Location: <dd> Link to the location of the block' ...
              ' in your model.  Use the link "Clear Hilited Blocks"' ...
              'at the top of the page to unhilite all blocks.' ...
              ' (Note: you must use the MATLAB Help' ...
              ' browser to use these hyperlinks).\n']);
fprintf(fid, '</dl> \n');
fprintf(fid, ['Note: In accelerated mode, individual blocks will not' ...
              ' show up in the profiler, unless they are executed internally' ...
              ' in Simulink (e.g. a scope runs in Simulink instead' ...
              ' of the generated code).  Rerun in normal mode to' ...
              ' get a more detailed analysis of the simulation.\n']);

fprintf(fid, '<h2> Model execution pseudocode: </h2>\n');
fprintf(fid, '<ul>\n');
fprintf(fid, '<li>Sim()\n');
fprintf(fid, '<ul>\n');
fprintf(fid, '<li>ModelInitialize() - Set up the model for simulation.\n');
fprintf(fid, ['<li>ModelExecute() - Advance in time from '...
              't = Tstart to Tfinal.\n']);
fprintf(fid, '<ul>\n');
fprintf(fid, ['<li>Output() - Execute the output methods of blocks' ...
              ' in the model at time t.\n']);
fprintf(fid, ['<li>Update() - Execute the update methods of blocks' ...
              ' in the model at time t.\n']);
fprintf(fid, '<li>Integrate states from t to tnew (minor time steps).\n');
fprintf(fid, '<ul>\n');
fprintf(fid, ['<li> Compute states from derivs using repeated calls' ...
              ' to:\n']);
fprintf(fid, '<ul>\n');
fprintf(fid, '<li>MinorOutput()\n');
fprintf(fid, '<li>MinorDeriv()\n');
fprintf(fid, '</ul>\n');
fprintf(fid, ['<li>Locate any zero crossings; reset t and the' ...
              ' states accordingly using repeated calls to:\n']);
fprintf(fid, '<ul>\n');
fprintf(fid, '<li>MinorOutput()\n');
fprintf(fid, '<li>MinorZeroCrossings()\n');
fprintf(fid, '</ul>\n');
fprintf(fid, '</ul>\n');
fprintf(fid, '<li>EndIntegrate\n');
fprintf(fid, '<li>Set time t = tnew.\n'); 
fprintf(fid, '</ul>\n');
fprintf(fid, '<li>EndModelExecute\n');
fprintf(fid, '</ul>\n');
fprintf(fid, '<li>EndSim\n');
fprintf(fid, '</ul>\n');

fprintf(fid, '</body>\n');
fprintf(fid, '</html>\n');

fclose(fid);


%%%
%%% Anchor
%%%
function out = Anchor(str,name)
% Turns a string into an HTML named anchor.

out = sprintf('<a name="%s"> %s </a>', name, str);

%%%
%%% PrintHTMLFunctionList
%%%
function PrintHTMLFunctionList(pname, fid, detailsName, structArray, clockPrec)
% Print the list of functions in a table.

digits1 = ceil(-log10(clockPrec));
digits2 = digits1 + ceil(log10(max([structArray.NumCalls])));

[junk,sortedIdx] = sort([structArray.TotalTime]);
sortedIdx = sortedIdx(end:-1:1);

totalTime = sum([structArray.SelfTime]);
if (totalTime == 0)
    totalTime = eps;
end

fprintf(fid, '<h2> %s </h2> \n\n', Anchor('Function List', 'Function List'));

fprintf(fid, '<table border=1>\n');
fprintf(fid, '<th align=left> <small> Name </small> </th>\n');
fprintf(fid, '<th align=left colspan=2> <small> Time </small> </th>\n');
fprintf(fid, '<th align=left> <small> Calls </small> </th>\n');
fprintf(fid, '<th align=left> <small> Time/call </small> </th>\n');
fprintf(fid, '<th align=left colspan=2> <small> Self time </small> </th>\n');

loc = 'Location';
if strcmp(pname,'Simulink')
  loc = [loc, ' (must use MATLAB Help browser to view)'];
end
fprintf(fid, ['<th align=left> <small> ', loc, ' </small> </th>\n']);

fprintf(fid, '</tr>\n');

for k = 1:length(structArray)
    thisStruct = structArray(sortedIdx(k));
    href = sprintf('%s#Fcn_%d', detailsName, sortedIdx(k));
    
    fprintf(fid, '<tr>\n');

    fprintf(fid, '<td>%s</td> \n', ...
                    Link(FormatName(thisStruct.FunctionName),href));
    fprintf(fid, '<td align="right"> <small> %s </small> </td>\n', ...
            FormatTime(thisStruct.TotalTime, digits1));
    fprintf(fid, '<td align="right"> <small> %5.1f%% </small> </td>\n', ...
            thisStruct.TotalTime * 100 / totalTime);
    fprintf(fid, '<td align="right"> <small> %d </small> </td>\n', ...
            thisStruct.NumCalls);
    fprintf(fid, '<td align="right"> <small> %s </small> </td>\n', ...
            FormatTime(thisStruct.TotalTime / thisStruct.NumCalls, digits2));
    fprintf(fid, '<td align="right"> <small> %s </small> </td>\n', ...
            FormatTime(thisStruct.SelfTime, digits1));
    fprintf(fid, '<td align="right"> <small> %5.1f%% </small> </td>\n', ...
            thisStruct.SelfTime * 100 / totalTime);
    if (~isempty(thisStruct.FileName))
        fprintf(fid, '<td> %s </td>\n', FormatPath(pname,thisStruct.FileName));
    else
        fprintf(fid, '<td> <small> <em> %s </em> </small> </td>\n', ...
                thisStruct.Type);
    end
    
    fprintf(fid, '</tr>\n');
end

fprintf(fid, '</table>\n');

%%%
%%% Link
%%%
function out = Link(in,href)
% Turn a string into a cross-reference.

out = sprintf('<a href="%s">%s</a>', href, in);

%%%
%%% FormatName
%%%
function out = FormatName(name)
% Format a function name with the right color and font.

out = sprintf('<font color="#0000FF"><tt><b>%s</b></tt></font>', ...
                name);

%%%
%%% FormatTime
%%%
function out = FormatTime(seconds, digits)
% Format a time string with the right number of decimal places.

% remove all -0.0 from report (just a cosmetic fix)
if(abs(seconds) < sqrt(eps))
  seconds = 0.0;
end

formatStr = sprintf('%%.%df', digits);
out = sprintf(formatStr, seconds);

%%%
%%% FormatPath
%%%
function out = FormatPath(pname,pathStr)
% Format a path string as a file URL.

  if strcmp(pname,'Simulink')

    newline = sprintf('\n');
    tab     = sprintf('\t');
  
    encodedPath = '';
    for i=1:length(pathStr)
      switch(pathStr(i))
       case '\'
        encodedPath(end+1:end+2) = '\\';
       case ' '
        encodedPath(end+1:end+2) = '\s';      
       case tab
        encodedPath(end+1:end+2) = '\t';
       case newline
        encodedPath(end+1:end+2) = '\n';
       case ''''
        encodedPath(end+1:end+2) = '\T';
       case '"'
        encodedPath(end+1:end+2) = '\Q';
       otherwise
        encodedPath(end+1) = pathStr(i);
      end
    end

    href = ['matlab: slprofile_hilite_system encoded-path ',encodedPath,';'];

  else
    href = ['file://' pathStr];
  end
  out = sprintf('<tt><a href="%s">%s</a></tt>', href, pathStr);

%endfunction FormatPath


%%%
%%% PrintHTMLFunctions
%%%
function PrintHTMLFunctionsFrame(path, detailsName, structArray, clockPrec, pname)
% Print the Function Details frame.

fid = fopen(fullfile(path,detailsName), 'wt');
if (fid < 0)
    error('Could not open report file for writing.');
end

wbHandle = waitbar(0,'Generating function details profile report ...');

fprintf(fid, '<html>\n');
fprintf(fid, '<head>\n');
fprintf(fid, '</head>\n');
fprintf(fid, '<body bgcolor="#FFF7DE" link="#247B2B" vlink="#247B2B" > \n\n');

[junk,sortedIdx] = sort([structArray.TotalTime]);
sortedIdx = sortedIdx(end:-1:1);

totalTime = sum([structArray.SelfTime]);

fprintf(fid, '<h1>%s Profile Report:  Function Details</h1>\n', pname);

fprintf(fid, '<dl>\n');
for k = 1:length(structArray)
    PrintHTMLFunctionDetails(fid, pname, structArray, sortedIdx(k), totalTime, clockPrec);
    waitbar(k/length(structArray));
end
fprintf(fid, '</dl>\n');

fprintf(fid, '</body>\n');
fprintf(fid, '</html>\n');

fclose(fid);

close(wbHandle);

%%%
%%% PrintHTMLFunctionDetails
%%%
function PrintHTMLFunctionDetails(fid, pname, structArray, p, totalTime, clockPrec)
% Print the detailed report for a given function.

funcStruct = structArray(p);
refStr = sprintf('Fcn_%d', p);
formattedName = FormatName(funcStruct.FunctionName);
digits1 = ceil(-log10(clockPrec));
digits2 = digits1 + ceil(log10(max([structArray.NumCalls])));

fprintf(fid, '<dt>\n');
fprintf(fid, '<hr size=5 noshade>\n');
if (isempty(funcStruct.FileName))
    fprintf(fid,'%s &nbsp;&nbsp;&nbsp;&nbsp;<em>%s</em><br>\n', ...
                    Anchor(formattedName, refStr), funcStruct.Type);
else
    fprintf(fid,'%s &nbsp;&nbsp;&nbsp;&nbsp;%s<br>\n', ...
                    Anchor(formattedName, refStr), ...
                    FormatPath(pname,funcStruct.FileName));
end

fprintf(fid, 'Time: %s s &nbsp;&nbsp; (%4.1f%%)<br>\n', ...
                FormatTime(funcStruct.TotalTime, digits1), ...
                100 * funcStruct.TotalTime / (max(totalTime, eps)));
fprintf(fid, 'Calls: %d <br>\n', funcStruct.NumCalls);
fprintf(fid, 'Self time: %s s &nbsp;&nbsp; (%4.1f%%)<br><br>\n', ...
                FormatTime(funcStruct.SelfTime, digits1), ...
                100 * funcStruct.TotalTime / (max(totalTime, eps)));

fprintf(fid, '<dd>\n');
fprintf(fid, '<table border=1>\n');
fprintf(fid, '<th align=left> <small> Function: </small> </th>\n');
fprintf(fid, '<th align=left colspan=2> <small> Time </small> </th>\n');
fprintf(fid, '<th align=left> <small> Calls </small> </th>\n');
fprintf(fid, '<th align=left> <small> Time/call </small> </th>\n');
fprintf(fid, '</tr>\n');

fprintf(fid, '<tr>\n');
fprintf(fid, '<td> %s </td>\n', formattedName);
fprintf(fid, '<td align=right> <small> %s </small> </td>\n', ...
                FormatTime(funcStruct.TotalTime, digits1));
fprintf(fid, '<td> &nbsp; </td>\n');
fprintf(fid, '<td align=right> <small> %d </small>\n', funcStruct.NumCalls);
fprintf(fid, '<td align=right> <small> %s </small> </td>\n', ...
                FormatTime(funcStruct.TotalTime / funcStruct.NumCalls, digits2));
fprintf(fid, '</tr>\n');

fprintf(fid, '<tr> <td colspan=5> &nbsp; </tr>\n');

fprintf(fid, '<tr> <td colspan=5> <small> <b>Parent functions:</b> </small> </td> </tr>\n');
if (length(funcStruct.Parents) == 0)
    fprintf(fid, '<tr> <td colspan=5> <small> <em> none </em> </small> </td> </tr> \n');
else
    for k = 1:length(funcStruct.Parents)
        href = sprintf('#Fcn_%d', funcStruct.Parents(k).Index);
        parentName = FormatName(structArray(funcStruct.Parents(k).Index).FunctionName);
        fprintf(fid, '<tr>\n');
        fprintf(fid, '<td colspan=3> %s </td>\n', Link(parentName,href));
        fprintf(fid, '<td align=right> <small> %d </small> </td>\n', ...
                        funcStruct.Parents(k).NumCalls);
        fprintf(fid, '<td> &nbsp; </td>\n');
        fprintf(fid, '<tr>\n');
    end
end

fprintf(fid, '<tr> <td colspan=5> &nbsp; </tr>\n');

fprintf(fid, '<tr> <td colspan=5> <small> <b>Child functions:</b> </small> </td> </tr>\n');
kids = funcStruct.Children;
if (length(kids) == 0)
    fprintf(fid, '<tr> <td colspan=5> <small> <em> none </em> </small> </td> </tr> \n');
else
    [junk,idx] = sort(-[kids.TotalTime]);
    kids = kids(idx);
    for k = 1:length(kids)
        href = sprintf('#Fcn_%d', kids(k).Index);
        childName = FormatName(structArray(kids(k).Index).FunctionName);
        fprintf(fid, '<tr>\n');
        fprintf(fid, '<td> %s </td>\n', Link(childName,href));
        fprintf(fid, '<td align=right> <small> %s </small> </td>\n', ...
                        FormatTime(kids(k).TotalTime, digits1));
        fprintf(fid, '<td align=right> <small> %4.1f%% </small> </td>\n', ...
                        100 * kids(k).TotalTime / max(funcStruct.TotalTime, eps));
        fprintf(fid, '<td align=right> <small> %d </small> </td>\n', ...
                        kids(k).NumCalls);
        fprintf(fid, '<td align=right> <small> %s </small> </td>\n', ...
                        FormatTime(kids(k).TotalTime / ...
                        kids(k).NumCalls, digits2));
        fprintf(fid, '<tr>\n');
    end
end
fprintf(fid, '</table><br><br>\n');

stats = LineStats(pname, funcStruct);
if (~isempty(stats))
    totalLineTime = sum([stats{:,2}]);
    fprintf(fid, '%d%% of the total time in this function was spent on the following lines:<br><br>\n',...
                    floor(100 * totalLineTime / max(funcStruct.TotalRecursiveTime,eps)));
    fprintf(fid, '<table border=0> \n');
    for k = 1:size(stats,1)
        lineNum = stats{k,1};
        lineTime = stats{k,2};
        lineString = stats{k,3};
        
        if ((k > 1) & (lineNum > (stats{k-1,1} + 1)))
            fprintf(fid, '<tr> <td> &nbsp; </td> </tr> \n');
        end
        
        fprintf(fid, '<tr> \n');
        
        if (lineTime > 0)
            fprintf(fid, '<td align=right> <code> %s </code> </td>\n', ...
                            FormatTime(lineTime, digits1));
            fprintf(fid, '<td align=right> <code> %d%% </code> </td>\n', ...
                            round(100 * lineTime / ...
                            max(funcStruct.TotalRecursiveTime, eps)));
        else
            fprintf(fid, '<td> &nbsp; </td>\n');
            fprintf(fid, '<td> &nbsp; </td>\n');
        end
        
        fprintf(fid, '<td align=right> <code> %d:&nbsp; </code> </td>\n', ...
                        lineNum);
        % Replace tabs by 2 spaces, then replace spaces by
        % nonbreaking spaces.
        lineString = strrep(lineString,char(9),'  ');
        lineString = strrep(lineString,' ','&nbsp;');
        lineString = strrep(lineString,'%<','%&lt;');
        fprintf(fid, '<td> <code> <nobr> %s </nobr> </code> </td> %\n', ...
                                         lineString);
        
        fprintf(fid, '</tr> \n');
    end
    fprintf(fid, '</table>\n');
end

        

function [counts,typenames] = CountFuncs(structArray)
% Count the number of functions of each type.

typenames = { structArray.Type };
typenames = unique(typenames);  % sort too?
numnames = length(typenames);
counts = zeros(numnames,1);

for k = 1:length(structArray)
  idx = strmatch(structArray(k).Type, typenames, 'exact');
  counts(idx) = counts(idx) + 1;
end

function structArray = MungeNames(structArray)
% MATLAB stores names for private functions as the full path; strip
% out the path part.

s = ['private' filesep];

for k = 1:length(structArray)
  if (~strcmp(structArray(k).FunctionName, filesep))
    idx = findstr(s, structArray(k).FunctionName);
    if (~isempty(idx))
      structArray(k).FunctionName = structArray(k).FunctionName(idx(end) + ...
                                                  length(s):end);
    end
  end
end

function newArray = ComputeTimes(structArray)
% Compute self time and total recursive time.

newArray = structArray;

for k = 1:length(newArray)
    childTime = 0;
    for p = 1:length(newArray(k).Children)
        childTime = childTime + newArray(k).Children(p).TotalTime;
    end
    newArray(k).TotalChildrenTime = childTime;
    newArray(k).SelfTime = newArray(k).TotalRecursiveTime - ...
            childTime;
end

%%%
%%% LineStats
%%%
function stats = LineStats(pname, funcStruct)
% Return P-by-3 cell array.  First column is the line number.  Second column
% is the line time.  Third column is the line string.  P is the the number
% of lines accounting for 95% of the total time, or 10 lines, or the total
% number of lines in the file, whichever is smaller.

if (isempty(funcStruct.ExecutedLines))
    stats = cell(0,3);
else
    
    % Check for P-file.
    if (strcmp('MATLAB',pname) & strcmpi(funcStruct.FileName(end-1:end),'.p'))
        funcStruct.FileName = [funcStruct.FileName(1:end-2) '.m'];
        fid = fopen(funcStruct.FileName);
        if (fid < 0)
            stats = cell(0,3);
        end
    else
        fid = fopen(funcStruct.FileName);
        if (fid < 0)
            stats = cell(0, 3);
        end
    end

    if (fid >= 0)
        comp = computer;
        inMat = textread(funcStruct.FileName,'%s','delimiter','\n','whitespace','');
	% this is a bit of a hack to fix LINENO pcode ops that are
        % generated for empty lines at the end of a file
	inMat{length(inMat)+1} = '';
        fclose(fid);

        timePerLine(funcStruct.ExecutedLines(:,1)) = funcStruct.ExecutedLines(:,3);
        timePerLine = timePerLine(:);
        totalTime = max(sum(timePerLine),eps);
        numInputLines = length(timePerLine);
        
        [bb, mfileIdx] = sort(timePerLine);
        bb = flipud(bb);
        mfileIdx = flipud(mfileIdx);
        
        % Report on the lines accounting for 95% of the total time, or 10
        % lines, or the total number of lines in the file, whichever
        % is smaller.
        numBusyLines = min(min(length(find((cumsum(bb)/max(totalTime,eps))>.95)), ...
                        10), numInputLines);
        bb = bb(1:numBusyLines);
        mfileIdx = mfileIdx(1:numBusyLines);
        
        %---------------------------------------------------------
        % Find the context lines, storing them in mfileLines.
        
        if (~isempty(mfileIdx))
            % Get rid of lines with no sample hits.
            mfileIdx(bb==0) = [];
            bb(bb==0) = [];
            
            mfileLines = max(1, [mfileIdx-1 ; mfileIdx ; mfileIdx+1]);
            
            % Remove out of range lines
            mfileLines((mfileLines < 1) | (mfileLines > numInputLines)) = [];
            
            mfileLines = sort(mfileLines);
            d = find(abs(diff(mfileLines))==0);
            mfileLines(d) = [];
        else
            mfileLines = [];
        end
        
        stats = cell(length(mfileLines), 3);
        for m = 1:length(mfileLines)
	    nextLine = inMat{mfileLines(m)};
            k = find(mfileIdx == mfileLines(m));
            if (isempty(k))
                lineTime = 0;
            else
                lineTime = bb(k);
            end
            stats{m,1} = mfileLines(m);
            stats{m,2} = lineTime;
            stats{m,3} = nextLine;
        end
    end
end

function [fname,structArray,history,clockPrec,name,msg] = ParseInputs(varargin)

fname = '';
structArray = [];
history = [];
clockPrec = [];
msg = '';
name = '';
h = 0.0;

msg = nargchk(0,2,nargin);
if (~isempty(msg))
    return;
end

if (nargin == 0)
    % PROFREPORT
    fname = tempname;
    callstats('stop');
    [ft,fh,cp,name] = callstats('stats');
    info.FunctionTable = ft;
    info.FunctionHistory = fh;
    info.ClockPrecision = cp;
    info.Name = name;

elseif (nargin == 1)
    if (ischar(varargin{1}))
        % PROFREPORT(BASENAME)
        fname = varargin{1};
        callstats('stop');
        [ft,fh,cp,name] = callstats('stats');
        info.FunctionTable = ft;
        info.FunctionHistory = fh;
        info.ClockPrecision = cp;
        info.Name = name;
        
    elseif (isstruct(varargin{1}))
        % PROFREPORT(INFO)
        callstats('stop');
        fname = tempname;
        info = varargin{1};

    elseif (isnumeric(varargin{1}))
        % PROFREPORT(HANDLE)
        h = varargin{1};
        fname = tempname;
        callstats(h,'stop');
        [ft,fh,cp,name] = callstats(h,'stats');
        info.FunctionTable = ft;
        info.FunctionHistory = fh;
        info.ClockPrecision = cp;
        info.Name = name;

    else
        msg = 'First input to PROFREPORT must be INFO struct or BASENAME string';
        return
    end
    
elseif (nargin == 2)
    % PROFREPORT(BASENAME,INFO) or PROFREPORT(H,BASENAME)
    if (~ischar(varargin{1}) & isnumeric(varargin{1}))
        h = varargin{1};
        
        if (ischar(varargin{2}))
            % PROFREPORT(H,BASENAME)
            fname = varargin{2};
            callstats(h,'stop');
            [ft,fh,cp,name] = callstats(h,'stats');
            info.FunctionTable = ft;
            info.FunctionHistory = fh;
            info.ClockPrecision = cp;
            info.Name = name;
        else
            msg = 'Second input to PROFREPORT must be a BASENAME srting when the first input is a handle';
            return
        end
        
    else
        if (~ischar(varargin{1}))
            msg = 'Expected BASENAME string as first input';
            return
        end
        
        if (~isstruct(varargin{2}))
            msg = 'Expected INFO struct as second input';
            return
        end
        
        callstats('stop');
        fname = varargin{1};
        info = varargin{2};
        
    end
    
end

if isempty(info.FunctionTable)
    msg = sprintf('%s\n%s\n%s\n%s', ...
                  'The input profiler log is empty, so no report',...
                  'can be generated.  Sometimes this is caused by',...
                  'forgetting to call PROFILE ON.  See the',...
                  'reference page for PROFILE.');
    return
end

structArray = info.FunctionTable;
history = info.FunctionHistory;
clockPrec = info.ClockPrecision;
name = info.Name;
