%--------------------------------------------
%
% TeX profiling library
% 
% Copyright 2010 by Christian Feuersänger.
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
% 
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
% 
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.
%
%--------------------------------------------

\pgfutil@ifundefined{pdfelapsedtime}{%
	\PackageError{pgf}{library 'profiler' can only be used with pdftex Rev. >= 1.671 because it needs the \string\pdfelapsedtime\space command}{}%
	\global\let\pdfelapsedtime=\c@pgf@counta
}{}


% Defines a new profiler entry named `#1'.
%
% This allocates a set of counters.
\def\pgfprofilenew#1{%
	\pgfutil@ifundefined{c@pgfprofile@elapsedtotal@#1}{%
		\expandafter\gdef\csname c@pgfprofile@elapsedtotal@#1\endcsname{0}%
		\expandafter\gdef\csname c@pgfprofile@elapsedself@#1\endcsname{0}%
		\expandafter\gdef\csname c@pgfprofile@numinvocations@#1\endcsname{0}%
		\expandafter\gdef\csname c@pgfprofile@subtractforself@#1\endcsname{0}%
		\expandafter\gdef\csname c@pgfprofile@semaphor@#1\endcsname{0}%
		\expandafter\gdef\csname c@pgfprofile@elapsed@at@start@#1\endcsname{0}%
		\pgfprofile@all@registered@list@add{#1}%
	}{%
		% it is already defined and registered. Shouldn't hurt, might
		% even be a good feature.
	}%
}%

\def\pgfprofile@TeX@DIALECT@toenvironment@begin#1{\csname #1\endcsname}%
\def\pgfprofile@TeX@DIALECT@toenvironment@end#1{\csname end#1\endcsname}%

\def\pgfprofile@@to@pgfretval#1{%
	\expandafter\let\expandafter\pgfretval#1%
}%

% Defines a new profiler entry for the environment `#1'.
%
% This calls \pgfprofilenew and handles the begin/end of the
% environment automatically.
%
% #1 the environment name (a string)
%
% Optionally in square brackets: the profiler entry name
% \pgfprofilenewforenvironment{<environment name>}
% \pgfprofilenewforenvironment[<profiler entry name>]{<environment name>}
\def\pgfprofilenewforenvironment{\pgfutil@ifnextchar[{\pgfprofilenewforenvironment@}{\pgfprofilenewforenvironment@[]}}%
\def\pgfprofilenewforenvironment@[#1]#2{%
	\expandafter\pgfprofile@@to@pgfretval\pgfprofile@TeX@DIALECT@toenvironment@begin{#2}%
	\ifx\pgfretval\relax
		\PackageError{pgf}{\string\pgfprofilenewforenvironment{#2} doesn't work: the environment `#2' is (not yet?) known or not known in this context}{}%
	\else
		\expandafter\global\expandafter\let\csname pgfprofile@orig@begin@#2\endcsname=\pgfretval
		\expandafter\pgfprofile@@to@pgfretval\pgfprofile@TeX@DIALECT@toenvironment@end{#2}%
		\expandafter\global\expandafter\let\csname pgfprofile@orig@end@#2\endcsname=\pgfretval
		%
		\def\pgfprofile@temp{#1}%
		\ifx\pgfprofile@temp\pgfutil@empty
			\pgfprofilenewforenvironment@@{\pgfprofileenv #2}{#2}%
		\else
			\pgfprofilenewforenvironment@@{#1}{#2}%
		\fi
	\fi
}%
\def\pgfprofilenewforenvironment@@#1#2{%
	\pgfprofilenew{#1}%
	\expandafter\expandafter\expandafter\gdef\pgfprofile@TeX@DIALECT@toenvironment@begin{#2}{%
		\pgfprofilestart{#1}%
		\csname pgfprofile@orig@begin@#2\endcsname
	}%
	\expandafter\expandafter\expandafter\gdef\pgfprofile@TeX@DIALECT@toenvironment@end{#2}{%
		\csname pgfprofile@orig@end@#2\endcsname
		\pgfprofileend{#1}%
	}%
}


\def\pgfprofilecs{<CS>}%
\def\pgfprofileenv{<ENV>}%

% Defines a new profiler entry for the control sequence '#1'.
%
% This calls \pgfprofilenew and enhances the control sequence with
% support for timings.
% 
% #1: the control sequence (with backslash!)
% #2: the number of arguments (see below).
% 
% The following commands are supported:
% - commands which take one (optional) argument in square brackets,
% - commands which take one (optional) argument in square brackets
%   followed by one optional argument which has to be delimited with
%   curly braces (use an empty argument for '#2' in this case),
% - commands which take one (optional) argument in square brackets
%   and *exactly* #2 arguments afterwards.
\def\pgfprofilenewforcommand{\pgfutil@ifnextchar[{\pgfprofilenewforcommand@}{\pgfprofilenewforcommand@[]}}
\def\pgfprofilenewforcommand@[#1]#2#3{%
	\def\pgfprofile@temp{#3}%
	\ifx\pgfprofile@temp\pgfutil@empty
		\pgfprofilenewforcommandpattern[#1]{#2}{<autocheck>}{}%
	\else
		\ifcase#3\relax
			\pgfprofilenewforcommandpattern[#1]{#2}{}{}%
		\or
			\pgfprofilenewforcommandpattern[#1]{#2}{##1}{{##1}}%
		\or
			\pgfprofilenewforcommandpattern[#1]{#2}{##1##2}{{##1}{##2}}%
		\or
			\pgfprofilenewforcommandpattern[#1]{#2}{##1##2##3}{{##1}{##2}{##3}}%
		\or
			\pgfprofilenewforcommandpattern[#1]{#2}{##1##2##3##4}{{##1}{##2}{##3}{##4}}%
		\or
			\pgfprofilenewforcommandpattern[#1]{#2}{##1##2##3##4##5}{{##1}{##2}{##3}{##4}{##5}}%
		\or
			\pgfprofilenewforcommandpattern[#1]{#2}{##1##2##3##4##5##6}{{##1}{##2}{##3}{##4}{##5}{##6}}%
		\or
			\pgfprofilenewforcommandpattern[#1]{#2}{##1##2##3##4##5##6##7}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}}%
		\else
			\PackageError{pgf}{library 'profiler' can't replace control sequences with '#3' arguments automatically, sorry.}%
		\fi
	\fi
}%

% \pgfprofilenewforcommandpattern[<profile entry name>]{<\CS>}{<definition pattern>}{<invocation pattern>}
%
% example:
% \pgfprofilenewforcommandpattern{\pgfqkeys}{##1##2}{{##1}{##2}}
%
% \def\mymacro##1\to##2\in##3{ .... }
% \pgfprofilenewforcommandpattern{\mymacro}{##1\to##2\in##3}{{##1}\to{##2}\in{##3}}
\def\pgfprofilenewforcommandpattern{\pgfutil@ifnextchar[{\pgfprofilenewforcommandpattern@}{\pgfprofilenewforcommandpattern@[]}}
\def\pgfprofilenewforcommandpattern@[#1]#2#3#4{%
	\def\pgfprofile@known{1}%
	\ifx#2\undefined
		\def\pgfprofile@known{0}%
	\else
		\ifx#2\relax
			\def\pgfprofile@known{0}%
		\fi
	\fi
	\if\pgfprofile@known0%
		\PackageError{pgf}{\string\pgfprofilenewforcommandpattern{\string#2} doesn't work: the command `\string#2' is (not yet?) known or not known in this context}{}%
	\else
	\begingroup
	\pgfprofile@cs@to@name{#2}%
	\let\pgfprofilenew@cmdname=\pgfretval
	%
	\def\pgfprofile@temp{#1}%
	\ifx\pgfprofile@temp\pgfutil@empty
		\edef\pgfprofilenew@profilerentryname{\pgfprofilecs\pgfprofilenew@cmdname}%
	\else
		\edef\pgfprofilenew@profilerentryname{#1}%
	\fi
	\expandafter\global\expandafter\let\csname pgfprofile@name@for@\pgfprofilenew@cmdname\endcsname=\pgfprofilenew@profilerentryname
	\pgfprofilenew{\pgfprofilenew@profilerentryname}%
	\expandafter\gdef\csname b@pgfprofile@show@\pgfprofilenew@profilerentryname\endcsname{0}%
	%
	\expandafter\global\expandafter\let\csname pgfprofile@orig@\pgfprofilenew@cmdname\endcsname=#2%
	\toks0={#3}%
	\edef\pgfprofile@temp{\the\toks0}%%
	\def\pgfprofile@tempb{<autocheck>}%
	\ifx\pgfprofile@temp\pgfprofile@tempb
		\expandafter\global\expandafter\let\csname pgfprofile@repl@\pgfprofilenew@cmdname\endcsname=\pgfprofileinvokecommand@uptoonearg
	\else
		\expandafter\gdef\csname pgfprofile@repl@\pgfprofilenew@cmdname\endcsname#3{%
			\pgfprofile@invokeorig{#4}%
		}%
	\fi
	\xdef#2{\noexpand\pgfprofileinvokecommand{\pgfprofilenew@cmdname}}%
	\endgroup
	\fi
}%

\def\pgfprofileshowinvocationsfor#1{%
	\pgfutil@ifundefined{b@pgfprofile@show@#1}{%
		\PackageError{pgf}{\string\pgfprofileshowinvocationsfor{#1} doesn't work: the argument is no profiler entry}{}%
	}{%
		\expandafter\gdef\csname b@pgfprofile@show@#1\endcsname{1}%
		\expandafter\gdef\csname b@pgfprofile@show@edef@#1\endcsname{0}%
	}%
}%
\def\pgfprofileshowinvocationsexpandedfor#1{%
	\pgfutil@ifundefined{b@pgfprofile@show@#1}{%
		\PackageError{pgf}{\string\pgfprofileshowinvocationsfor{#1} doesn't work: the argument is no profiler entry}{}%
	}{%
		\expandafter\gdef\csname b@pgfprofile@show@#1\endcsname{1}%
		\expandafter\gdef\csname b@pgfprofile@show@edef@#1\endcsname{1}%
	}%
}%

\def\pgfprofile@no@optional@arg@text{<noarg>}
\def\pgfprofileinvokecommand#1{%
	\pgfutil@ifnextchar[{\pgfprofileinvokecommand@{#1}}{\pgfprofileinvokecommand@{#1}[<noarg>]}%
}%
\def\pgfprofileinvokecommand@#1[#2]{%
	\def\pgfprofileinvokecommand@cs{#1}%
	\def\pgfprofileinvokecommand@optarg{#2}%
	\edef\pgfprofileinvokecommand@profilerentryname{\csname pgfprofile@name@for@#1\endcsname}%
	\csname pgfprofile@repl@#1\endcsname%
}%

% #1 contains ALL arguments, including any braces.
\def\pgfprofile@invokeorig#1{%
	% re-insert the control sequence name and the optional argument:
	% this wasn't possible directly.
	\begingroup
	\toks1=\expandafter{\pgfprofileinvokecommand@optarg}%
	\toks2={#1}%
	\toks3=\expandafter{\csname pgfprofile@orig@\pgfprofileinvokecommand@cs\endcsname}%
	%
	\ifx\pgfprofileinvokecommand@optarg\pgfprofile@no@optional@arg@text
		% no [] argument after original command:
		\xdef\pgfprofile@invokeorig@doitfinally{\the\toks3 \the\toks2 }%%
	\else
		\xdef\pgfprofile@invokeorig@doitfinally{\the\toks3[\the\toks1]\the\toks2 }%
	\fi
	\if1\csname b@pgfprofile@show@\pgfprofileinvokecommand@profilerentryname\endcsname
		\pgfprofile@orig@show
	\fi
	\endgroup
	\edef\pgfprofile@temp{{\pgfprofileinvokecommand@cs}{\pgfprofileinvokecommand@profilerentryname}}%
	\expandafter\pgfprofile@invokeorig@\pgfprofile@temp%
}%
\def\pgfprofile@orig@show{%
	\toks4=\expandafter{\csname\pgfprofileinvokecommand@cs\endcsname}%
	\ifx\pgfprofileinvokecommand@optarg\pgfprofile@no@optional@arg@text
		\def\pgfprofile@orig@show@args{\the\toks2 }%
	\else
		\def\pgfprofile@orig@show@args{[\the\toks1] \the\toks2 }%
	\fi
	\if1\csname b@pgfprofile@show@edef@\pgfprofileinvokecommand@profilerentryname\endcsname
		\edef\pgfprofile@orig@show@args{\pgfprofile@orig@show@args}%
		\edef\pgfprofile@orig@show@args{\pgfprofile@orig@show@args}%
	\fi
	\immediate\write16{pgflibraryprofiler: calling 
		\#\csname c@pgfprofile@numinvocations@\pgfprofileinvokecommand@profilerentryname\endcsname\space
		(\the\toks4 \pgfprofile@orig@show@args)}%
}%

% #1: control sequence name (without backslash)
% #2: profiler entry name
\def\pgfprofile@invokeorig@#1#2{%
	% this check should avoid save stack impact
	\pgfprofileifisrunning{#2}{%
		\pgfprofile@invokeorig@doitfinally%
	}{%
		\pgfprofilestart{#2}%
		\pgfprofile@invokeorig@doitfinally%
		\pgfprofileend{#2}%
	}%
}%


\def\pgfprofileinvokecommand@uptoonearg{%
	\pgfutil@ifnextchar\bgroup{\pgfprofileinvokecommand@onearg}{\pgfprofileinvokecommand@noarg}%
}%
\def\pgfprofileinvokecommand@onearg#1{%
	\pgfprofile@invokeorig{{#1}}%
}%
\def\pgfprofileinvokecommand@noarg{%
	\pgfprofile@invokeorig{}%
}%



% Starts / Resumes timing of the profiler entry named `#1'.
%
% The timing will continue until \pgfprofileend{#1} is called.
%
% Nested calls of \pgfprofilestart{#1} (with the same argument) will
% result in the same result as if just one \pgfprofilestart command
% has been issued.
\def\pgfprofilestart#1{%
	\pgfprofileifisrunning{#1}{%
		\relax
	}{%
		\expandafter\xdef\csname c@pgfprofile@elapsed@at@start@#1\endcsname{\the\pdfelapsedtime}%
		\expandafter\gdef\csname c@pgfprofile@subtractforself@#1\endcsname{0}%
		\pgfprofile@advance{c@pgfprofile@numinvocations@#1}{1}%
		\pgfprofilestackpush{#1}%
	}%
	\pgfprofile@advance{c@pgfprofile@semaphor@#1}{1}%
}%

% Stops / Interrupts timing of the profiler entry named `#1'.
\def\pgfprofileend#1{%
	\pgfprofile@advance{c@pgfprofile@semaphor@#1}{-1}%
	\pgfprofileifisrunning{#1}{%
		\relax
	}{%
		\begingroup
			\c@pgf@countb=\pdfelapsedtime\relax
			\advance\c@pgf@countb by-\csname c@pgfprofile@elapsed@at@start@#1\endcsname\relax
			%
			\pgfprofilestackpop\pgfretval
			\edef\pgfprofile@temp{#1}%
			\ifx\pgfprofile@temp\pgfretval
			\else
				\immediate\write16{pgflibraryprofiler WARNING: possible error in self time computation...}%
			\fi
			\pgfprofilestackifempty{%
				\relax
			}{%
				\pgfprofilestacktop\pgfretval
				\pgfprofile@advance{c@pgfprofile@subtractforself@\pgfretval}{\c@pgf@countb}%
			}%
			%
			\pgfprofile@advance{c@pgfprofile@elapsedtotal@#1}{\c@pgf@countb}%
			\advance\c@pgf@countb by-\csname c@pgfprofile@subtractforself@#1\endcsname\relax
			\pgfprofile@advance{c@pgfprofile@elapsedself@#1}{\c@pgf@countb}%
		\endgroup
	}%
}%

% invokes '#2' if '#1' is currently running and '#3' if not.
\def\pgfprofileifisrunning#1#2#3{%
	\ifnum\csname c@pgfprofile@semaphor@#1\endcsname=0 \def\pgfprofileifisrunning@next{#3}\else \def\pgfprofileifisrunning@next{#2}\fi
	\pgfprofileifisrunning@next
}%

% Sets the profiler entry whose total time is used to compute all
% other relative times.
\def\pgfprofilesetrel#1{\edef\pgfprofile@rel{#1}}%

% Stops all running timings and postprocesses them.
\def\pgfprofilepostprocess{%
	\begingroup
	%
	% prepare files.
	\c@pgf@countb=\time
	\divide\c@pgf@countb by60
	\c@pgf@countd=\c@pgf@countb
	\multiply\c@pgf@countd by60
	\c@pgf@countc=\time
	\advance\c@pgf@countc by-\c@pgf@countd
	\immediate\openout\w@pgf@writea=\jobname.profiler.\the\year-\pgfprofiletotwodigitstr\month-\pgfprofiletotwodigitstr\day_\pgfprofiletotwodigitstr\c@pgf@countb h_\pgfprofiletotwodigitstr\c@pgf@countc m.dat
	\immediate\write\w@pgf@writea{%
		\pgfprofile@percent relative values are measured against the totaltime of `\pgfprofile@rel'.%
	}%
	\immediate\write16{pgflibraryprofiler: relative values are measured against the totaltime of `\pgfprofile@rel'.}%
	\immediate\write\w@pgf@writea{%
		profilerentry\pgfprofile@TAB 
		totaltime[s]\pgfprofile@TAB 
		totaltime[percent]\pgfprofile@TAB
		selftime[s]\pgfprofile@TAB
		selftime[percent]\pgfprofile@TAB
		numinvocations\pgfprofile@TAB}%
	%
	%
	% compute main time and prepare computation of relative times:
	\pgfprofileifisrunning{\pgfprofile@rel}{%
		\pgfprofileend{\pgfprofile@rel}%
	}{}%
	\pgf@xa=\csname c@pgfprofile@elapsedtotal@\pgfprofile@rel\endcsname sp
	\edef\pgfprofiletotaltime{\pgf@sys@tonumber\pgf@xa}%
	\pgfmathreciprocal@{\pgfprofiletotaltime}%
	\let\pgfprofiletotaltime@inv=\pgfmathresult
	%
	% postprocess each of them:
	\pgfprofile@all@registered@list@foreach{%
		\pgfprofileifisrunning{##1}{%
			\pgfprofileend{##1}%
		}{}%
		\pgfprofilepostprocess@single{##1}%
	}%
	\immediate\write\w@pgf@writea{%
		\pgfprofile@percent\space vim: ts=40 nowrap nostartofline
	}%
	\immediate\closeout\w@pgf@writea
	\endgroup
}%

{%
\catcode`\[=1 %
\catcode`\]=2 %
\catcode`\{=12 %
\catcode`\}=12 %
\catcode`\^^I=12
\gdef\pgfprofile@lbrace[{]%
\gdef\pgfprofile@rbrace[}]%
\gdef\pgfprofile@TAB[^^I]%
]
{
\catcode`\%=12 \gdef\pgfprofile@percent{%}}

\def\pgfprofilepostprocess@single#1{%
	\begingroup
	\pgf@xa=\csname c@pgfprofile@elapsedtotal@#1\endcsname sp
	\pgf@xb=\csname c@pgfprofile@elapsedself@#1\endcsname sp
	\pgf@ya=\pgfprofiletotaltime@inv\pgf@xa
	\pgf@yb=\pgfprofiletotaltime@inv\pgf@xb
	\multiply\pgf@ya by100
	\multiply\pgf@yb by100
	\edef\pgfprofilecur@total{\pgf@sys@tonumber\pgf@xa}%
	\edef\pgfprofilecur@self{\pgf@sys@tonumber\pgf@xb}%
	\edef\pgfprofilecur@total@rel{\pgf@sys@tonumber\pgf@ya}%
	\edef\pgfprofilecur@self@rel{\pgf@sys@tonumber\pgf@yb}%
	\immediate\write16{
		pgflibraryprofiler(#1) 
		\pgfprofile@lbrace 
			total time=\pgfprofilecur@total sec; (\pgfprofilecur@total@rel\pgfprofile@percent)
			self time=\pgfprofilecur@self sec; (\pgfprofilecur@self@rel\pgfprofile@percent)
			invocations=\csname c@pgfprofile@numinvocations@#1\endcsname;
		\pgfprofile@rbrace
	}%
	\immediate\write\w@pgf@writea{%
		#1\pgfprofile@TAB 
		\pgfprofilecur@total\pgfprofile@TAB 
		\pgfprofilecur@total@rel\pgfprofile@TAB
		\pgfprofilecur@self\pgfprofile@TAB
		\pgfprofilecur@self@rel\pgfprofile@TAB
		\csname c@pgfprofile@numinvocations@#1\endcsname\pgfprofile@TAB
	}%
	\endgroup
}%
% Invokes '#2' for each element of the command separated list '#1'.
% the current list element is available as '#1' inside of '#2'.
\long\def\pgfprofileforeachentryinCSV#1#2{%
	\long\def\pgfprofileinvokeforeach@@##1{#2}%
	\pgfprofileforeachentryinCSVisterminated@loop#1,\pgfeov
}%
\long\def\pgfprofileforeachentryinCSVisterminated@loop{%
	\pgfutil@ifnextchar\pgfeov{\pgfutil@gobble}{\pgfprofileforeachentryinCSV@next}%
}%
\long\def\pgfprofileforeachentryinCSV@next#1,{%
	\pgfprofileinvokeforeach@@{#1}%
	\pgfprofileforeachentryinCSVisterminated@loop%
}%

\xdef\pgfprofile@all@registered@list{}
\xdef\pgfprofile@currently@running@list{}

\def\pgfprofile@all@registered@list@add#1{%
	\ifx\pgfprofile@all@registered@list\pgfutil@empty
		\xdef\pgfprofile@all@registered@list{#1}%
	\else
		\xdef\pgfprofile@all@registered@list{\pgfprofile@all@registered@list,#1}%
	\fi
}%

\def\pgfprofile@all@registered@list@foreach#1{%
	\expandafter\pgfprofileforeachentryinCSV\expandafter{\pgfprofile@all@registered@list}{#1}%
}%

\def\pgfprofile@advance#1#2{%
	\begingroup
		\c@pgf@counta=\csname #1\endcsname\relax
		\advance\c@pgf@counta by#2\relax
		\expandafter\xdef\csname #1\endcsname{\the\c@pgf@counta}%
	\endgroup
}%

\def\pgfprofile@cs@to@name@#1#2\relax{\def\pgfretval{#2}}

% defines '\pgfretval' to be the control sequences name of '#1' *without* the backslash.
\def\pgfprofile@cs@to@name#1{%
	\expandafter\pgfprofile@cs@to@name@\string#1\relax
}%

\newcount\c@pgfprofile@stacktop

\c@pgfprofile@stacktop=-1
\def\pgfprofilestackpush#1{%
	\global\advance\c@pgfprofile@stacktop by1
	\expandafter\xdef\csname pgfprofile@stack@\the\c@pgfprofile@stacktop\endcsname{#1}%
}%
% pops the current stack top to macro #1
\def\pgfprofilestackpop#1{%
	\pgfprofilestacktop#1%
	\global\advance\c@pgfprofile@stacktop by-1
}%
% returns the stack's top to macro #1
\def\pgfprofilestacktop#1{%
	\expandafter\let\expandafter#1\csname pgfprofile@stack@\the\c@pgfprofile@stacktop\endcsname
}%
\def\pgfprofilestackifempty#1#2{%
	\ifnum\c@pgfprofile@stacktop<0 #1\else #2\fi
}%


\def\pgfprofiletotwodigitstr#1{\ifnum#1<10 0\the#1\else\the#1\fi}

\pgfprofilenew{main job}
\pgfprofilestart{main job}
\expandafter\xdef\csname c@pgfprofile@elapsed@at@start@main job\endcsname{0}%
\pgfprofilesetrel{main job}

\pgfutil@ifundefined{AtEndDocument}{%
	% no latex. ok.
}{%
	% do latex specific stuff:
	\pgfprofilenew{preamble}
	\pgfprofilestart{preamble}
	\expandafter\xdef\csname c@pgfprofile@elapsed@at@start@preamble\endcsname{0}%
	\AtBeginDocument{\pgfprofileend{preamble}}%
	%
	\AtEndDocument{\pgfprofilepostprocess}%
}%
\endinput
