diff options
author | Karl Berry <karl@freefriends.org> | 2020-08-17 20:58:45 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2020-08-17 20:58:45 +0000 |
commit | 6df02fac79cff54b64d93b90e234904331b6d7bf (patch) | |
tree | f1f2bba9cdba7c86bb2be98299615d416de40f1d /Master/texmf-dist/tex/latex/algpseudocodex | |
parent | ca2a3cf1cffa8b48f35aaa7a5dac389c0a95a79b (diff) |
algpseudocodex (17aug20)
git-svn-id: svn://tug.org/texlive/trunk@56125 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/algpseudocodex')
-rw-r--r-- | Master/texmf-dist/tex/latex/algpseudocodex/algpseudocodex.sty | 735 |
1 files changed, 735 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/latex/algpseudocodex/algpseudocodex.sty b/Master/texmf-dist/tex/latex/algpseudocodex/algpseudocodex.sty new file mode 100644 index 00000000000..e6da8cd0c9f --- /dev/null +++ b/Master/texmf-dist/tex/latex/algpseudocodex/algpseudocodex.sty @@ -0,0 +1,735 @@ +%% 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}{}% + }{}% +} |