634 lines
26 KiB
TeX
634 lines
26 KiB
TeX
% Pseudocodex Algorithmic Style
|
|
%
|
|
% Copyright 2017 Christian Matt
|
|
% Based on Szasz Janos' algpseudocode.sty
|
|
%
|
|
|
|
\NeedsTeXFormat{LaTeX2e}
|
|
\ProvidesPackage{algpseudocodex}
|
|
\RequirePackage{algorithmicx}
|
|
\RequirePackage{etoolbox}
|
|
\RequirePackage{fifo-stack}
|
|
\RequirePackage{varwidth}
|
|
\RequirePackage{tikz}
|
|
\usetikzlibrary{calc,fit,tikzmark}
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
% Package options
|
|
%
|
|
\newbool{algpx@noEnd}
|
|
\newbool{algpx@indLine}
|
|
|
|
\DeclareOption{noend}{\setbool{algpx@noEnd}{true}}
|
|
\DeclareOption{end}{\setbool{algpx@noEnd}{false}}
|
|
\DeclareOption{noindline}{\setbool{algpx@indLine}{false}}
|
|
\DeclareOption{indline}{\setbool{algpx@indLine}{true}}
|
|
\ProcessOptions*
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
% Styles
|
|
%
|
|
\tikzset{%
|
|
defaultCodeBox/.style={draw},%
|
|
algpxIndentLine/.style={draw=lightgray,very thin}%
|
|
}
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
% Declarations
|
|
%
|
|
\algnewlanguage{pseudocodex}
|
|
\alglanguage{pseudocodex}
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
% Macros for boxes around code
|
|
%
|
|
|
|
% \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
|
|
\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}
|
|
\newcounter{algpx@codeBoxCount}
|
|
\newcounter{algpx@nestedCBoxCount}
|
|
\newcounter{algpx@startedBoxesCount}
|
|
\newcounter{algpx@endedBoxesCount}
|
|
\newcounter{algpx@nestedBoxedStringCount}
|
|
\newcounter{algpx@nestedBoxedStringMaxCount}
|
|
\newlength{\algpx@oldPos}
|
|
\newlength{\algpx@newPos}
|
|
\newlength{\algpx@tmpLen}
|
|
\FSCreate{algpx@startNewCodeBoxQueue}{0}
|
|
\FSCreate{algpx@codeBoxStack}{0}
|
|
\FSCreate{algpx@codeBoxStackTmp}{0}
|
|
|
|
\newsavebox{\algpx@boxedStringBox}
|
|
|
|
|
|
\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}{%
|
|
\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}%
|
|
% create box from here to end of line
|
|
\begin{varwidth}[t]{\dimexpr \linewidth - \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}%
|
|
}%
|
|
}
|
|
|
|
\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}%
|
|
}{}%
|
|
}
|
|
|
|
% must be followed by \State, \If, \For etc.
|
|
\newcommand{\BeginBox}[1][defaultCodeBox]{%
|
|
\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}%
|
|
}
|
|
|
|
\newcommand{\EndBox}{%
|
|
\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%
|
|
\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}{}%
|
|
}%
|
|
}%
|
|
}
|
|
|
|
\newcommand{\BoxedString}[2][defaultCodeBox]{%
|
|
\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
|
|
%
|
|
|
|
\newcounter{algpx@indentCount}
|
|
\FSCreate{algpx@indentStack}{0}
|
|
\newbool{algpx@indJustEnded} % true after end with noend
|
|
\newcounter{algpx@justUsedIndentCount}
|
|
\newlength{\algpx@indStartY}
|
|
\newlength{\algpx@indEndY}
|
|
\newlength{\algpx@indStartX} % x coordinate of indent line
|
|
|
|
% start indented block
|
|
\newcommand{\algpx@startIndent}{%
|
|
\ifbool{algpx@indLine}{%
|
|
\@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
|
|
\newcommand{\algpx@endIndent}[1][0]{%
|
|
\ifbool{algpx@indLine}{%
|
|
\ifbool{algpx@noEnd}{%
|
|
\setlength{\algpx@minIndDist}{1pt}%
|
|
}{%
|
|
\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)$);%
|
|
}%
|
|
}{}%
|
|
}%
|
|
}%
|
|
\ifboolexpr{not bool{algpx@indJustEnded} and bool{algpx@noEnd} and test{\ifstrequal{#1}{0}}}{%
|
|
\setbool{algpx@indJustEnded}{true}%
|
|
\setcounter{algpx@justUsedIndentCount}{\FSTop{algpx@indentStack}}%
|
|
}{}%
|
|
\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
|
|
%
|
|
\algdef{SE}[WHILE]{While}{EndWhile}[1]{%
|
|
\algpx@startIndent\algpx@startCodeCommand\algorithmicwhile\ #1\ \algorithmicdo%
|
|
}{%
|
|
\algpx@endIndent\algpx@setCodeBoxWest\algorithmicend\ \algorithmicwhile%
|
|
}
|
|
\algdef{SE}[FOR]{For}{EndFor}[1]{%
|
|
\algpx@startIndent\algpx@startCodeCommand\algorithmicfor\ #1\ \algorithmicdo%
|
|
}{%
|
|
\algpx@endIndent\algpx@setCodeBoxWest\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@setCodeBoxWest\algorithmicend\ \algorithmicloop%
|
|
}
|
|
\algdef{SE}[REPEAT]{Repeat}{Until}{%
|
|
\algpx@startIndent\algpx@startCodeCommand\algorithmicrepeat%
|
|
}[1]{%
|
|
\algpx@endIndent\algpx@setCodeBoxWest\algorithmicuntil\ #1%
|
|
}
|
|
\algdef{SE}[IF]{If}{EndIf}[1]{%
|
|
\algpx@startIndent\algpx@startCodeCommand\algorithmicif\ #1\ \algorithmicthen%
|
|
}{%
|
|
\algpx@endIndent\algpx@setCodeBoxWest\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@setCodeBoxWest\algorithmicend\ \algorithmicprocedure%
|
|
}
|
|
\algdef{SE}[FUNCTION]{Function}{EndFunction}[2]{%
|
|
\algpx@startIndent\algpx@startCodeCommand\algorithmicfunction\ \textproc{#1}\ifstrempty{#2}{}{(#2)}%
|
|
}{%
|
|
\algpx@endIndent\algpx@setCodeBoxWest\algorithmicend\ \algorithmicfunction%
|
|
}
|
|
|
|
\ifbool{algpx@noEnd}{%
|
|
\algtext*{EndWhile}%
|
|
\algtext*{EndFor}%
|
|
\algtext*{EndLoop}%
|
|
\algtext*{EndIf}%
|
|
\algtext*{EndProcedure}%
|
|
\algtext*{EndFunction}%
|
|
%
|
|
\pretocmd{\EndWhile}{\algpx@endIndent}{}{}%
|
|
\pretocmd{\EndFor}{\algpx@endIndent}{}{}%
|
|
\pretocmd{\EndLoop}{\algpx@endIndent}{}{}%
|
|
\pretocmd{\EndIf}{\algpx@endIndent}{}{}%
|
|
\pretocmd{\EndProcedure}{\algpx@endIndent}{}{}%
|
|
\pretocmd{\EndFunction}{\algpx@endIndent}{}{}%
|
|
}{}%
|
|
|
|
\apptocmd{\State}{\algpx@startCodeCommand}{}{}
|
|
|
|
\pretocmd{\State}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\While}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\EndWhile}{\algpx@endCodeCommand[\ifbool{algpx@noEnd}{1}{0}]}{}{}
|
|
\pretocmd{\For}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\EndFor}{\algpx@endCodeCommand[\ifbool{algpx@noEnd}{1}{0}]}{}{}
|
|
\pretocmd{\ForAll}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\Loop}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\EndLoop}{\algpx@endCodeCommand[\ifbool{algpx@noEnd}{1}{0}]}{}{}
|
|
\pretocmd{\Repeat}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\Until}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\If}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\ElsIf}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\Else}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\EndIf}{\algpx@endCodeCommand[\ifbool{algpx@noEnd}{1}{0}]}{}{}
|
|
\pretocmd{\Procedure}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\EndProcedure}{\algpx@endCodeCommand[\ifbool{algpx@noEnd}{1}{0}]}{}{}
|
|
\pretocmd{\Function}{\algpx@endCodeCommand}{}{}
|
|
\pretocmd{\EndFunction}{\algpx@endCodeCommand[\ifbool{algpx@noEnd}{1}{0}]}{}{}
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
% 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}}{}%
|
|
\ifbool{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)}}
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
% 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}{}%
|
|
}{}%
|
|
}
|
|
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
% Patch varwidth to allow \hfill
|
|
\newif\if@useMaxWidth
|
|
\AtBeginEnvironment{varwidth}{%
|
|
\@useMaxWidthfalse%
|
|
\renewcommand{\hfill}{\global\@useMaxWidthtrue\hspace{\fill}}%
|
|
}
|
|
|
|
% only set width to measured width if hfill was not used
|
|
\patchcmd{\endvarwidth}{\ifdim\@vwid@<\hsize\hsize\@vwid@\fi}%
|
|
{\ifdim\@vwid@<\hsize\if@useMaxWidth\else\hsize\@vwid@\fi\fi}%
|
|
{}{\PackageWarning{algpseudocodex}{Failed to patch varwidth. \hfill probably does not work inside algorithmic.}}
|
|
|
|
\algrenewcommand{\algorithmiccomment}[1]{{\color{gray}// #1}} |