%% algpseudocodex.sty %% Copyright 2017, 2020 Christian Matt % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Christian Matt. % % This work consists of the files algpseudocodex.sty and documentation.tex. % Pseudocodex algorithmic style % Based on Szasz Janos' algpseudocode.sty \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{algpseudocodex}[2020-08-16 v1.0 pseudocode typesetting] \RequirePackage{kvoptions} \RequirePackage{algorithmicx} \RequirePackage{etoolbox} \RequirePackage{fifo-stack} \RequirePackage{varwidth} \RequirePackage{tabto} \RequirePackage{tikz} \usetikzlibrary{calc,fit,tikzmark} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Package options % \SetupKeyvalOptions{% family=algpx,% prefix=algpx@% } \DeclareBoolOption[true]{noEnd}% don't print end if etc. \DeclareBoolOption[true]{indLines}% show indent guide lines \DeclareBoolOption[true]{spaceRequire}% adds vertical space before \Require if not in first line \DeclareBoolOption[true]{italicComments}% italicise comments \DeclareBoolOption[true]{rightComments}% right justify comments \DeclareStringOption[gray]{commentColor}% color of comments \DeclareStringOption[$\triangleright$~]{beginComment}% print at beginning of comment \DeclareStringOption[]{endComment}% print at end of comment \DeclareStringOption[$\triangleright$~]{beginLComment}% print at beginning of long comment \DeclareStringOption[~$\triangleleft$]{endLComment}% print at end of long comment \ProcessLocalKeyvalOptions* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Styles % \tikzset{% algpxDefaultBox/.style={draw},% algpxIndentLine/.style={draw=gray,very thin}% } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Declarations % \algnewlanguage{pseudocodex} \alglanguage{pseudocodex} % \tikzmark is only available on next compilation; the following command immediately after using it \newcommand{\@tikzmarkNow}[2][]{\tikz[overlay,remember picture] \node[#1] (#2) {};} \long\def\@ifnodedefined#1#2#3{% \@ifundefined{pgf@sh@ns@#1}{#3}{#2}% } \newlength{\algpx@codeBoxInnerSep}% distance between box and its content \newlength{\algpx@codeBoxSep}% distance between nested boxes \newlength{\algpx@codeBoxOuterSep}% additional space before and after box \newlength{\algpx@minIndDist}% minimum distance between start and end of indent line to draw line \newlength{\algpx@oldPos} \newlength{\algpx@newPos} \newlength{\algpx@tmpLen}% length to be used for various things \newlength{\algpx@tmpLenA}% another tmp length \newlength{\algpx@indStartY} \newlength{\algpx@indEndY} \newlength{\algpx@indStartX}% x coordinate of indent line \setlength{\algpx@codeBoxInnerSep}{2pt} \setlength{\algpx@codeBoxSep}{3pt} \setlength{\algpx@codeBoxOuterSep}{1pt} \newbool{algpx@firstLine} \newbool{algpx@setNorth} \newbool{algpx@executeEndVarwidth} \newbool{algpx@adjustHeight} \newbool{algpx@restorePrevdepth} \newbool{algpx@indJustEnded}% true after end with noend \newcounter{algpx@codeBoxCount}% number of already created code boxes. Used to fill algpx@startNewCodeBoxQueue \newcounter{algpx@nestedCBoxCount} \newcounter{algpx@startedBoxesCount} \newcounter{algpx@endedBoxesCount} \newcounter{algpx@nestedBoxedStringCount} \newcounter{algpx@nestedBoxedStringMaxCount} \newcounter{algpx@indentCount} \newcounter{algpx@justUsedIndentCount} \FSCreate{algpx@startNewCodeBoxQueue}{0}% queue of code boxes to be created on next \State, \If etc. Only stores index of box (0 is value of top if empty; should not be relevant) \FSCreate{algpx@codeBoxStack}{0} \FSCreate{algpx@codeBoxStackTmp}{0} \FSCreate{algpx@indentStack}{0} \newsavebox{\algpx@boxedStringBox} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Macros for boxes around code % % Draw a box. First parameter is style, others are coordinates (north, west, east, south) \newcommand{\algpx@drawCodeBox}[5]{% \tikz[overlay,remember picture]{% \node[inner sep=\algpx@codeBoxInnerSep,#1,fit={(pic cs:#2) (pic cs:#3) (pic cs:#4) (pic cs:#5)}] {};% }% } % execute after \State, \If etc. \newcommand{\algpx@startCodeCommand}{% \algpx@startCodeCommandX{}{}% } % extended version of \algpx@startCodeCommand % first argument is printed before content % second argument is used to reserve space after contents (for end symbol of long comments) \newcommand{\algpx@startCodeCommandX}[2]{% \setbool{algpx@indJustEnded}{false}% \setcounter{algpx@startedBoxesCount}{0}% \setcounter{algpx@endedBoxesCount}{0}% \setcounter{algpx@nestedBoxedStringMaxCount}{0}% \whileboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@startNewCodeBoxQueue}}}}{% \FSPush{algpx@codeBoxStack}{\FSTop{algpx@startNewCodeBoxQueue}}% \algpx@drawCodeBox{algpx@codeBoxStyle\FSTop{algpx@startNewCodeBoxQueue}}{algpx@codeBoxNorth-\FSTop{algpx@startNewCodeBoxQueue}}{algpx@codeBoxWest-\FSTop{algpx@startNewCodeBoxQueue}}{algpx@codeBoxEast-\FSTop{algpx@startNewCodeBoxQueue}}{algpx@codeBoxSouth-\FSTop{algpx@startNewCodeBoxQueue}}% \FSPop{algpx@startNewCodeBoxQueue}% \setbool{algpx@setNorth}{true}% \stepcounter{algpx@startedBoxesCount}% }% \algpx@setCodeBoxWest% \setbool{algpx@firstLine}{false}% \setbool{algpx@executeEndVarwidth}{true}% #1% % create box from here to end of line, leaving space for #2 \settowidth{\algpx@tmpLen}{#1}% \settowidth{\algpx@tmpLenA}{#2}% \begin{varwidth}[t]{\dimexpr \linewidth - \algpx@tmpLen - \algpx@tmpLenA - \algorithmicindent * \numexpr \value{ALG@nested} - 1 \relax \relax}% } % executed before \State, \If etc., i.e., at end of previous line % first argument 1 if after \EndIf etc. and noend \newcommand{\algpx@endCodeCommand}[1][0]{% \ifbool{algpx@executeEndVarwidth}{% \par\xdef\algpx@pdtemp{\the\prevdepth}% see https://tex.stackexchange.com/a/34982 \end{varwidth}\setbox0=\lastbox\usebox0% \setbool{algpx@executeEndVarwidth}{false}% \setbool{algpx@adjustHeight}{true}% \setbool{algpx@restorePrevdepth}{true}% }{}% \ifbool{algpx@setNorth}{% % todo: north should not be set again if highest element in current line is BoxedString \algpx@setCodeBoxNorth{\the\ht0}% set north here shifted by height of current line \setbool{algpx@setNorth}{false}% }{}% \algpx@setCodeBoxEast% \ifbool{algpx@adjustHeight}{% % todo: if highest or deepest element in current line is BoxedString, \ht0 or \dp0 should be adjusted for different sep values \algpx@addBoxSpacing{\value{algpx@startedBoxesCount}}{\the\ht0}{\value{algpx@endedBoxesCount}}{\the\dp0}% \setbool{algpx@adjustHeight}{false}% }{}% \ifboolexpr{bool{algpx@restorePrevdepth} and test{\ifstrequal{#1}{0}}}{% \par\prevdepth\algpx@pdtemp% \setbool{algpx@restorePrevdepth}{false}% }{}% } % set algpx@codeBoxNorthMax of current box and all ancestors; argument is amount to shift up from current baseline \newcommand{\algpx@setCodeBoxNorth}[1]{% \setcounter{algpx@nestedCBoxCount}{0}% \whileboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@codeBoxStack}}}}{% \FSPush{algpx@codeBoxStackTmp}{\FSTop{algpx@codeBoxStack}}% copy stack to tmp \@ifnodedefined{algpx@codeBoxNorthMax-\FSTop{algpx@codeBoxStack}}{% % get current position; shift according to nest-level to ensure nested boxes don't collide \@tikzmarkNow[yshift=\dimexpr #1 + \algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxNorthHere}% % extract y-coordinate of previously stored north \tikz[overlay,remember picture]{% \pgfextracty{\algpx@oldPos}{\pgfpointanchor{algpx@codeBoxNorthMax-\FSTop{algpx@codeBoxStack}}{center}}% \pgfextracty{\algpx@newPos}{\pgfpointanchor{algpx@codeBoxNorthHere}{center}}% \global\algpx@oldPos=\algpx@oldPos% \global\algpx@newPos=\algpx@newPos% }% \ifdimcomp{\algpx@oldPos}{<}{\algpx@newPos}{% % new y is greater than old one, so set it \@tikzmarkNow[yshift=\dimexpr #1 + \algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxNorthMax-\FSTop{algpx@codeBoxStack}}% }{% \@tikzmarkNow{algpx@codeBoxNorthDummy}% set something to keep in sync }% }{% \@tikzmarkNow[yshift=\dimexpr #1 + \algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxNorthMax-\FSTop{algpx@codeBoxStack}}% }% \FSPop{algpx@codeBoxStack}% \stepcounter{algpx@nestedCBoxCount}% }% % restore stack from tmp \whileboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@codeBoxStackTmp}}}}{% \FSPush{algpx@codeBoxStack}{\FSTop{algpx@codeBoxStackTmp}}% \FSPop{algpx@codeBoxStackTmp}% }% } % set algpx@codeBoxSouthMax of current box and all ancestors; argument is amount to shift down from current baseline \newcommand{\algpx@setCodeBoxSouth}[1]{% \setcounter{algpx@nestedCBoxCount}{0}% \whileboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@codeBoxStack}}}}{% \FSPush{algpx@codeBoxStackTmp}{\FSTop{algpx@codeBoxStack}}% copy stack to tmp \@ifnodedefined{algpx@codeBoxSouthMax-\FSTop{algpx@codeBoxStack}}{% % get current position; shift according to nest-level to ensure nested boxes don't collide \@tikzmarkNow[yshift=\dimexpr -#1 -\algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxSouthHere}% % extract y-coordinate of previously stored south \tikz[overlay,remember picture]{% \pgfextracty{\algpx@oldPos}{\pgfpointanchor{algpx@codeBoxSouthMax-\FSTop{algpx@codeBoxStack}}{center}}% \pgfextracty{\algpx@newPos}{\pgfpointanchor{algpx@codeBoxSouthHere}{center}}% \global\algpx@oldPos=\algpx@oldPos% \global\algpx@newPos=\algpx@newPos% }% \ifdimcomp{\algpx@oldPos}{>}{\algpx@newPos}{% % new y is less than old one, so set it \@tikzmarkNow[yshift=\dimexpr -#1 -\algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxSouthMax-\FSTop{algpx@codeBoxStack}}% }{% \@tikzmarkNow{algpx@codeBoxSouthDummy}% set something to keep in sync }% }{% \@tikzmarkNow[yshift=\dimexpr -#1 -\algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxSouthMax-\FSTop{algpx@codeBoxStack}}% }% \FSPop{algpx@codeBoxStack}% \stepcounter{algpx@nestedCBoxCount}% }% % restore stack from tmp \whileboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@codeBoxStackTmp}}}}{% \FSPush{algpx@codeBoxStack}{\FSTop{algpx@codeBoxStackTmp}}% \FSPop{algpx@codeBoxStackTmp}% }% } % set algpx@codeBoxWestMax of current box and all ancestors; argument is amount to shift left from current position \newcommand{\algpx@setCodeBoxWest}[1][0pt]{% \setcounter{algpx@nestedCBoxCount}{0}% \whileboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@codeBoxStack}}}}{% \FSPush{algpx@codeBoxStackTmp}{\FSTop{algpx@codeBoxStack}}% copy stack to tmp \@ifnodedefined{algpx@codeBoxWestMax-\FSTop{algpx@codeBoxStack}}{% % get current position; shift according to nest-level to ensure nested boxes don't collide \@tikzmarkNow[xshift=\dimexpr -#1 -\algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxWestHere}% % extract x-coordinate of previously stored west \tikz[overlay,remember picture]{% \pgfextractx{\algpx@oldPos}{\pgfpointanchor{algpx@codeBoxWestMax-\FSTop{algpx@codeBoxStack}}{center}}% \pgfextractx{\algpx@newPos}{\pgfpointanchor{algpx@codeBoxWestHere}{center}}% \global\algpx@oldPos=\algpx@oldPos% \global\algpx@newPos=\algpx@newPos% }% \ifdimcomp{\algpx@oldPos}{>}{\algpx@newPos}{% % new x is smaller than old one, so set it \@tikzmarkNow[xshift=\dimexpr -#1 -\algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxWestMax-\FSTop{algpx@codeBoxStack}}% }{% \@tikzmarkNow{algpx@codeBoxWestDummy}% set something to keep in sync }% }{% \@tikzmarkNow[xshift=\dimexpr -#1 -\algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxWestMax-\FSTop{algpx@codeBoxStack}}% }% \FSPop{algpx@codeBoxStack}% \stepcounter{algpx@nestedCBoxCount}% }% % restore stack from tmp \whileboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@codeBoxStackTmp}}}}{% \FSPush{algpx@codeBoxStack}{\FSTop{algpx@codeBoxStackTmp}}% \FSPop{algpx@codeBoxStackTmp}% }% } % set algpx@codeBoxEastMax of current box and all ancestors \newcommand{\algpx@setCodeBoxEast}{% \setcounter{algpx@nestedCBoxCount}{0}% \whileboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@codeBoxStack}}}}{% \FSPush{algpx@codeBoxStackTmp}{\FSTop{algpx@codeBoxStack}}% copy stack to tmp \@ifnodedefined{algpx@codeBoxEastMax-\FSTop{algpx@codeBoxStack}}{% % get current position; shift according to nest-level to ensure nested boxes don't collide \@tikzmarkNow[xshift=\dimexpr \algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxEastHere}% % extract x-coordinate of previously stored east \tikz[overlay,remember picture]{% \pgfextractx{\algpx@oldPos}{\pgfpointanchor{algpx@codeBoxEastMax-\FSTop{algpx@codeBoxStack}}{center}}% \pgfextractx{\algpx@newPos}{\pgfpointanchor{algpx@codeBoxEastHere}{center}}% \global\algpx@oldPos=\algpx@oldPos% \global\algpx@newPos=\algpx@newPos% }% \ifdimcomp{\algpx@oldPos}{<}{\algpx@newPos}{% % new x is greater than old one, so set it \@tikzmarkNow[xshift=\dimexpr \algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxEastMax-\FSTop{algpx@codeBoxStack}}% }{% \@tikzmarkNow{algpx@codeBoxEastDummy}% set something to keep in sync }% }{% \@tikzmarkNow[xshift=\dimexpr \algpx@codeBoxSep * \value{algpx@nestedCBoxCount}\relax]{algpx@codeBoxEastMax-\FSTop{algpx@codeBoxStack}}% }% \FSPop{algpx@codeBoxStack}% \stepcounter{algpx@nestedCBoxCount}% }% % restore stack from tmp \whileboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@codeBoxStackTmp}}}}{% \FSPush{algpx@codeBoxStack}{\FSTop{algpx@codeBoxStackTmp}}% \FSPop{algpx@codeBoxStackTmp}% }% } % Set coordinates of box on top of algpx@codeBoxStack to maximal reported values. % This ensures, e.g., that the right edge of the box is after the longest line inside. \newcommand{\algpx@setBoxesToStoredMax}{% \tikz[overlay,remember picture]{% \tikzmark{algpx@codeBoxNorth-\FSTop{algpx@codeBoxStack}}{(algpx@codeBoxNorthMax-\FSTop{algpx@codeBoxStack})}% \tikzmark{algpx@codeBoxSouth-\FSTop{algpx@codeBoxStack}}{(algpx@codeBoxSouthMax-\FSTop{algpx@codeBoxStack})}% \tikzmark{algpx@codeBoxWest-\FSTop{algpx@codeBoxStack}}{(algpx@codeBoxWestMax-\FSTop{algpx@codeBoxStack})}% \tikzmark{algpx@codeBoxEast-\FSTop{algpx@codeBoxStack}}{(algpx@codeBoxEastMax-\FSTop{algpx@codeBoxStack})}% }% } % add some space above and below box; more if several boxes are nested % first argument: number of boxes above % second argument: height of content % third argument: number of boxes below % fourth argument: depth of content \newcommand{\algpx@addBoxSpacing}[4]{% \ifnumcomp{0}{<}{#1}{% \rule{0pt}{\dimexpr #2 + \algpx@codeBoxInnerSep + \algpx@codeBoxOuterSep + (\algpx@codeBoxSep * (#1 - 1))\relax}% }{}% \ifnumcomp{0}{<}{#3}{% \rule[\dimexpr -#4 - \algpx@codeBoxInnerSep - \algpx@codeBoxOuterSep - (\algpx@codeBoxSep * (#3 - 1))\relax]{0pt}{\dimexpr #4 + \algpx@codeBoxInnerSep + \algpx@codeBoxOuterSep + (\algpx@codeBoxSep * (#3 - 1))\relax}% negative offset equal to height to draw line down from baseline }{}% } % Begin a new code box here. % Must be followed by \State, \If, \For etc. % (optional) argument is style of box \newcommand{\BeginBox}[1][algpxDefaultBox]{% \ignorespaces% \FSUnshift{algpx@startNewCodeBoxQueue}{\thealgpx@codeBoxCount}% add to queue; processed by \algpx@startCodeCommand %globally set tikz style (https://tex.stackexchange.com/a/47918) \begingroup% \globaldefs=1\relax% \pgfqkeys{/tikz}{algpx@codeBoxStyle\thealgpx@codeBoxCount/.style={#1}}% \endgroup% \stepcounter{algpx@codeBoxCount}% \ignorespaces% } % End one open code box. Fails if no box open (i.e., if algpx@codeBoxStack is empty). \newcommand{\EndBox}{% \ignorespaces% \ifnumcomp{0}{<}{\FSSize{algpx@startNewCodeBoxQueue}}{% \PackageError{algpseudocodex}{BeginBox must be followed by State, If, For, etc. Use BoxedString instead}{}% \FSClear{algpx@startNewCodeBoxQueue}% }{% \ifnumcomp{0}{<}{\FSSize{algpx@codeBoxStack}}{% \unskip% \ifbool{algpx@executeEndVarwidth}{% \par\xdef\algpx@pdtemp{\the\prevdepth}% see https://tex.stackexchange.com/a/34982 \end{varwidth}\setbox0=\lastbox\usebox0% \setbool{algpx@executeEndVarwidth}{false}% \setbool{algpx@adjustHeight}{true}% \setbool{algpx@restorePrevdepth}{true}% }{}% \ifbool{algpx@setNorth}{% \algpx@setCodeBoxNorth{\the\ht0}% set north here shifted by height of current line \setbool{algpx@setNorth}{false}% }{}% \algpx@setCodeBoxEast% \ifbool{algpx@indJustEnded}{% \algpx@setCodeBoxSouth{0pt}% set south here, don't shift after end with noEnd % add space after boxes. Otherwise done by endCodeCommand, but this is not executed after End with noEnd \algpx@addBoxSpacing{\value{algpx@startedBoxesCount}}{0pt}{\numexpr \value{algpx@endedBoxesCount} + 1 \relax}{0pt}% }{% \algpx@setCodeBoxSouth{\the\dp0}% set south here shifted by depth of current line }% %adjust stored prevdepth for ended boxes \ifnumcomp{0}{<}{\value{algpx@endedBoxesCount}}{% \edef\algpx@pdtemp{\dimexpr \algpx@pdtemp + \algpx@codeBoxSep \relax}% }{% \edef\algpx@pdtemp{\dimexpr \algpx@pdtemp + \algpx@codeBoxInnerSep + \algpx@codeBoxOuterSep \relax}% }% \algpx@setBoxesToStoredMax% \FSPop{algpx@codeBoxStack}% \stepcounter{algpx@endedBoxesCount}% }{% \PackageError{algpseudocodex}{No box to end}{}% }% }% \ignorespaces% } % Draw box within a single line of code. % (Optional) first argument is style of the box. % Second argument is the string to print inside the box. \newcommand{\BoxedString}[2][algpxDefaultBox]{% \ifnumcomp{\value{algpx@nestedBoxedStringCount}}{=}{0}{% \setcounter{algpx@nestedBoxedStringMaxCount}{0}% reset max count for new boxes }{}% \stepcounter{algpx@nestedBoxedStringCount}% \ifnumcomp{\value{algpx@nestedBoxedStringMaxCount}}{<}{\value{algpx@nestedBoxedStringCount}}{% \setcounter{algpx@nestedBoxedStringMaxCount}{\value{algpx@nestedBoxedStringCount}}% }{}% \algpx@drawCodeBox{#1}{algpx@codeBoxNorth-\thealgpx@codeBoxCount}{algpx@codeBoxWest-\thealgpx@codeBoxCount}{algpx@codeBoxEast-\thealgpx@codeBoxCount}{algpx@codeBoxSouth-\thealgpx@codeBoxCount}% \FSPush{algpx@codeBoxStack}{\thealgpx@codeBoxCount}% \stepcounter{algpx@codeBoxCount}% \algpx@setCodeBoxWest% \ifmmode% % This works in equation but not in align / displaystyle etc. gets lost \savebox{\algpx@boxedStringBox}{$\m@th#2$}% \usebox{\algpx@boxedStringBox}% \else% \savebox{\algpx@boxedStringBox}{#2}% \usebox{\algpx@boxedStringBox}% \fi% \algpx@setCodeBoxEast% \algpx@setCodeBoxNorth{\the\ht\algpx@boxedStringBox}% \algpx@setCodeBoxSouth{\the\dp\algpx@boxedStringBox}% \algpx@setBoxesToStoredMax% \FSPop{algpx@codeBoxStack}% \addtocounter{algpx@nestedBoxedStringCount}{-1}% BoxedString ends here \ifnumcomp{\value{algpx@nestedBoxedStringCount}}{=}{0}{% \algpx@addBoxSpacing{\value{algpx@nestedBoxedStringMaxCount}}{\the\ht\algpx@boxedStringBox}{\value{algpx@nestedBoxedStringMaxCount}}{\the\dp\algpx@boxedStringBox}% }{}% } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Macros for indentation lines % % start indented block \newcommand{\algpx@startIndent}{% \ifbool{algpx@indLines}{% \@tikzmarkNow{algpx@indentStart-\thealgpx@indentCount}% \FSPush{algpx@indentStack}{\thealgpx@indentCount}% \stepcounter{algpx@indentCount}% }{}% } % end of indented block; optional argument 1 means that only interrupted by else, 2 means ended with until \newcommand{\algpx@endIndent}[1][0]{% \ifbool{algpx@indLines}{% \ifboolexpr{bool{algpx@noEnd} and test{\ifstrequal{#1}{0}}}{% \setlength{\algpx@minIndDist}{1ex}% set mininum drawing distance to 1ex if ending without text }{% otherwise, factor in additional line for end, else, or until \setlength{\algpx@minIndDist}{\dimexpr \baselineskip + 1ex \relax}% }% \@tikzmarkNow{algpx@indentEnd-\FSTop{algpx@indentStack}}% \tikz[overlay,remember picture]{% \pgfextractx{\algpx@indStartX}{\pgfpointanchor{algpx@indentStart-\FSTop{algpx@indentStack}}{center}}% \ifbool{algpx@indJustEnded}{% % use same coordinates as for previous end \pgfextracty{\algpx@indEndY}{\pgfpointanchor{algpx@indentEnd-\thealgpx@justUsedIndentCount}{center}}% \draw[algpxIndentLine] ($(algpx@indentStart-\FSTop{algpx@indentStack})+(0.12em,-0.8ex)$) -- ($(\algpx@indStartX,\algpx@indEndY)+(0.12em,0ex)$) -- +(0.5em,0);% }{% % only draw if at least one line between start and end \pgfextracty{\algpx@indStartY}{\pgfpointanchor{algpx@indentStart-\FSTop{algpx@indentStack}}{center}}% \pgfextracty{\algpx@indEndY}{\pgfpointanchor{algpx@indentEnd-\FSTop{algpx@indentStack}}{center}}% \ifdimcomp{\algpx@indStartY-\algpx@minIndDist}{>}{\algpx@indEndY}{% \ifboolexpr{bool{algpx@noEnd} and test{\ifstrequal{#1}{0}}}{% % x of end is at end of line, extract x of start and use that instead \pgfextractx{\algpx@indStartX}{\pgfpointanchor{algpx@indentStart-\FSTop{algpx@indentStack}}{center}}% % draw additional line to right if noend and line is not interrupted \draw[algpxIndentLine] ($(algpx@indentStart-\FSTop{algpx@indentStack})+(0.12em,-0.8ex)$) -- ($(\algpx@indStartX,\algpx@indEndY)+(0.12em,0ex)$) -- +(0.5em,0);% }{% \draw[algpxIndentLine] ($(algpx@indentStart-\FSTop{algpx@indentStack})+(0.12em,-0.8ex)$) -- ($(algpx@indentEnd-\FSTop{algpx@indentStack})+(0.12em,2.0ex)$);% }% }{}% }% }% }{}% % set indJustEnded even if indLines are not drawn; also used for EndBox \ifboolexpr{not bool{algpx@indJustEnded} and bool{algpx@noEnd} and test{\ifstrequal{#1}{0}}}{% \setbool{algpx@indJustEnded}{true}% \setcounter{algpx@justUsedIndentCount}{\FSTop{algpx@indentStack}}% }{}% \ifbool{algpx@indLines}{% \FSPop{algpx@indentStack}% \ifstrequal{#1}{1}{% \algpx@startIndent% start new line if only interrupted }{}% }{}% } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Keywords % \algnewcommand\algorithmicend{\textbf{end}} \algnewcommand\algorithmicdo{\textbf{do}} \algnewcommand\algorithmicwhile{\textbf{while}} \algnewcommand\algorithmicfor{\textbf{for}} \algnewcommand\algorithmicforall{\textbf{for all}} \algnewcommand\algorithmicloop{\textbf{loop}} \algnewcommand\algorithmicrepeat{\textbf{repeat}} \algnewcommand\algorithmicuntil{\textbf{until}} \algnewcommand\algorithmicprocedure{\textbf{procedure}} \algnewcommand\algorithmicfunction{\textbf{function}} \algnewcommand\algorithmicif{\textbf{if}} \algnewcommand\algorithmicthen{\textbf{then}} \algnewcommand\algorithmicelse{\textbf{else}} \algnewcommand\algorithmicrequire{\textbf{Require:}} \algnewcommand\algorithmicensure{\textbf{Ensure:}} \algnewcommand\algorithmicreturn{\textbf{return}} \algnewcommand\algorithmicoutput{\textbf{output}} \algnewcommand\textproc{\textsc} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Loops, conditional statements, and functions % \algdef{SE}[WHILE]{While}{EndWhile}[1]{% \algpx@startIndent\algpx@startCodeCommand\algorithmicwhile\ #1\ \algorithmicdo% }{% \algpx@endIndent\algpx@startCodeCommand\algorithmicend\ \algorithmicwhile% } \algdef{SE}[FOR]{For}{EndFor}[1]{% \algpx@startIndent\algpx@startCodeCommand\algorithmicfor\ #1\ \algorithmicdo% }{% \algpx@endIndent\algpx@startCodeCommand\algorithmicend\ \algorithmicfor% } \algdef{S}[FOR]{ForAll}[1]{% \algpx@startIndent\algpx@startCodeCommand\algorithmicforall\ #1\ \algorithmicdo% } \algdef{SE}[LOOP]{Loop}{EndLoop}{% \algpx@startIndent\algpx@startCodeCommand\algorithmicloop% }{% \algpx@endIndent\algpx@startCodeCommand\algorithmicend\ \algorithmicloop% } \algdef{SE}[REPEAT]{Repeat}{Until}{% \algpx@startIndent\algpx@startCodeCommand\algorithmicrepeat% }[1]{% \algpx@endIndent[2]\algpx@startCodeCommand\algorithmicuntil\ #1% } \algdef{SE}[IF]{If}{EndIf}[1]{% \algpx@startIndent\algpx@startCodeCommand\algorithmicif\ #1\ \algorithmicthen% }{% \algpx@endIndent\algpx@startCodeCommand\algorithmicend\ \algorithmicif% } \algdef{C}[IF]{IF}{ElsIf}[1]{% \algpx@endIndent[1]\algpx@startCodeCommand\algorithmicelse\ \algorithmicif\ #1\ \algorithmicthen% } \algdef{Ce}[ELSE]{IF}{Else}{EndIf}{% \algpx@endIndent[1]\algpx@startCodeCommand\algorithmicelse% } \algdef{SE}[PROCEDURE]{Procedure}{EndProcedure}[2]{% \algpx@startIndent\algpx@startCodeCommand\algorithmicprocedure\ \textproc{#1}\ifstrempty{#2}{}{(#2)}% }{% \algpx@endIndent\algpx@startCodeCommand\algorithmicend\ \algorithmicprocedure% } \algdef{SE}[FUNCTION]{Function}{EndFunction}[2]{% \algpx@startIndent\algpx@startCodeCommand\algorithmicfunction\ \textproc{#1}\ifstrempty{#2}{}{(#2)}% }{% \algpx@endIndent\algpx@startCodeCommand\algorithmicend\ \algorithmicfunction% } \ifbool{algpx@noEnd}{% \algtext*{EndWhile}% \algtext*{EndFor}% \algtext*{EndLoop}% \algtext*{EndIf}% \algtext*{EndProcedure}% \algtext*{EndFunction}% % % end indent line after (not before), to get correct y position for multiline text in last command \apptocmd{\EndWhile}{\algpx@endIndent}{}{}% \apptocmd{\EndFor}{\algpx@endIndent}{}{}% \apptocmd{\EndLoop}{\algpx@endIndent}{}{}% \apptocmd{\EndIf}{\algpx@endIndent}{}{}% \apptocmd{\EndProcedure}{\algpx@endIndent}{}{}% \apptocmd{\EndFunction}{\algpx@endIndent}{}{}% }{}% % execute \algpx@endCodeCommand before \State, \If etc. \pretocmd{\State}{\algpx@endCodeCommand}{}{} \pretocmd{\While}{\algpx@endCodeCommand}{}{} \pretocmd{\For}{\algpx@endCodeCommand}{}{} \pretocmd{\ForAll}{\algpx@endCodeCommand}{}{} \pretocmd{\Loop}{\algpx@endCodeCommand}{}{} \pretocmd{\Repeat}{\algpx@endCodeCommand}{}{} \pretocmd{\Until}{\algpx@endCodeCommand}{}{} \pretocmd{\If}{\algpx@endCodeCommand}{}{} \pretocmd{\ElsIf}{\algpx@endCodeCommand}{}{} \pretocmd{\Else}{\algpx@endCodeCommand}{}{} \pretocmd{\Procedure}{\algpx@endCodeCommand}{}{} \pretocmd{\Function}{\algpx@endCodeCommand}{}{} % for end commands that may not be printed, tell endCodeCommand whether we are using noEnd \ifbool{algpx@noEnd}{% \pretocmd{\EndWhile}{\algpx@endCodeCommand[1]}{}{}% \pretocmd{\EndFor}{\algpx@endCodeCommand[1]}{}{}% \pretocmd{\EndLoop}{\algpx@endCodeCommand[1]}{}{}% \pretocmd{\EndIf}{\algpx@endCodeCommand[1]}{}{}% \pretocmd{\EndProcedure}{\algpx@endCodeCommand[1]}{}{}% \pretocmd{\EndFunction}{\algpx@endCodeCommand[1]}{}{}% }{% \pretocmd{\EndWhile}{\algpx@endCodeCommand[0]}{}{}% \pretocmd{\EndFor}{\algpx@endCodeCommand[0]}{}{}% \pretocmd{\EndLoop}{\algpx@endCodeCommand[0]}{}{}% \pretocmd{\EndIf}{\algpx@endCodeCommand[0]}{}{}% \pretocmd{\EndProcedure}{\algpx@endCodeCommand[0]}{}{}% \pretocmd{\EndFunction}{\algpx@endCodeCommand[0]}{}{}% }% % execute \algpx@startCodeCommand after \State (this is done for loops etc. inside the definitions above) \apptocmd{\State}{\algpx@startCodeCommand}{}{} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Other declarations % % dirty hack: execute drawing in argument of item and rest of \algpx@startCodeCommand outside % argument: text in item[.] \newcommand{\algpx@drawInItem}[1]{% \item[% \whileboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@startNewCodeBoxQueue}}}}{% \FSPush{algpx@codeBoxStack}{\FSTop{algpx@startNewCodeBoxQueue}}% \algpx@drawCodeBox{algpx@codeBoxStyle\FSTop{algpx@startNewCodeBoxQueue}}{algpx@codeBoxNorth-\FSTop{algpx@startNewCodeBoxQueue}}{algpx@codeBoxWest-\FSTop{algpx@startNewCodeBoxQueue}}{algpx@codeBoxEast-\FSTop{algpx@startNewCodeBoxQueue}}{algpx@codeBoxSouth-\FSTop{algpx@startNewCodeBoxQueue}}% \FSPop{algpx@startNewCodeBoxQueue}% \stepcounter{algpx@startedBoxesCount}% }% #1% ]% \setbool{algpx@indJustEnded}{false}% \setcounter{algpx@startedBoxesCount}{0}% \setcounter{algpx@endedBoxesCount}{0}% \setcounter{algpx@nestedBoxedStringMaxCount}{0}% \settowidth{\algpx@tmpLen}{#1}% \algpx@setCodeBoxWest[\dimexpr \labelsep + \algpx@tmpLen \relax]% \setbool{algpx@firstLine}{false}% \setbool{algpx@executeEndVarwidth}{true}% \begin{varwidth}[t]{\dimexpr \linewidth - \labelsep - \algpx@tmpLen + \leftmargin \relax}% \settoheight{\algpx@tmpLen}{#1}% \rule{0pt}{\algpx@tmpLen}% } \algnewcommand\Require{% \algpx@endCodeCommand% \ifnumcomp{0}{<}{\FSSize{algpx@startNewCodeBoxQueue}}{\setbool{algpx@setNorth}{true}}{}% \ifboolexpr{bool{algpx@spaceRequire} and not bool{algpx@firstLine}}{% \medskip% }{}% \algpx@drawInItem{\algorithmicrequire}% } \algnewcommand\Ensure{% \algpx@endCodeCommand% \ifnumcomp{0}{<}{\FSSize{algpx@startNewCodeBoxQueue}}{\setbool{algpx@setNorth}{true}}{}% \algpx@drawInItem{\algorithmicensure}% } \algnewcommand\Return{\algorithmicreturn{} } \algnewcommand\Output{\algorithmicoutput{} } \algnewcommand\Call[2]{\textproc{#1}\ifstrempty{#2}{}{(#2)}} %% internal methods to format comment % format according to comment format \newcommand{\algpx@commentFormat}[1]{% \ifbool{algpx@italicComments}{% \textit{\textcolor{\algpx@commentColor}{#1}}% }{% \textcolor{\algpx@commentColor}{#1}% }% } % get string for inline comment including begin and end markers \newcommand{\algpx@commentString}[1]{% \algpx@commentFormat{\algpx@beginComment#1\algpx@endComment}% } % redefine \Comment \algrenewcomment[1]{% \ifbool{algpx@rightComments}{% \settowidth{\algpx@tmpLen}{\algpx@commentString{#1}}% set temp variable to width of comment \tabto{\CurrentLineWidth}% this saves current position in \TabPrevPos and fixes spacing up to here \ifdimcomp{\algpx@tmpLen}{<}{\dimexpr \linewidth - \TabPrevPos \relax}{% check whether comment fits on line \tabto{\dimexpr \linewidth - \algpx@tmpLen \relax}% move position to right justify text \algpx@commentString{#1}% print comment }{% \algpx@commentString{#1}% if it doesn't fit, just print normally, allowing line break }% }{% \algpx@commentString{#1}% just print normally if not right justified }% \ignorespaces% }% % Long comment starting on new line. % Can span several lines and in contrast to \Comment, never right justified. \algdef{SL}[LCOMMENT]{LComment}{0}[1]{% \algpx@startCodeCommandX{\algpx@commentFormat{\algpx@beginLComment}}{\algpx@commentFormat{\algpx@endLComment}}% \algpx@commentFormat{#1}% print actual comment \tabto{\CurrentLineWidth}% this saves current position in \TabPrevPos and fixes spacing up to here \setlength{\algpx@tmpLen}{\dimexpr \linewidth - \TabPrevPos \relax}% set to remaining space on line \makebox[0pt][l]{% start box here that takes no space (otherwise impacts spacing of text before) \rule{\algpx@tmpLen}{0pt}% draw invisible rule from beginning of line until end of comment text \algpx@commentFormat{\algpx@endLComment}% print end comment at the end \algpx@setCodeBoxEast% since this takes 0 space, we have to set east of code box here explicitly }% }% \pretocmd{\LComment}{\algpx@endCodeCommand}{}{} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Execute at beginning and end of algorithmic environment % \AtBeginEnvironment{algorithmic}{% \setbool{algpx@firstLine}{true}% \setbool{algpx@setNorth}{false}% \setbool{algpx@executeEndVarwidth}{false}% \setbool{algpx@adjustHeight}{false}% \setbool{algpx@restorePrevdepth}{false}% \setcounter{algpx@startedBoxesCount}{0}% \setcounter{algpx@endedBoxesCount}{0}% } \AtEndEnvironment{algorithmic}{% \algpx@endCodeCommand% % Error checking \ifboolexpr{test{\ifnumcomp{0}{<}{\FSSize{algpx@codeBoxStack}}} or test {\ifnumcomp{0}{<}{\FSSize{algpx@startNewCodeBoxQueue}}}}{% \PackageError{algpseudocodex}{Some boxes have not ended}{}% }{}% }