% \iffalse meta-comment % % Copyright (C) 2007 by % Joseph Wright % % Released under the GNU General Public License v2 % See http://www.gnu.org/licenses/gpl.txt % % This work consists of the main source file unroman.dtx % and the derived files unroman.sty and unroman.pdf % %<*driver> \documentclass{ltxdoc} \EnableCrossrefs \CodelineIndex \RecordChanges %\OnlyDescription \usepackage[T1]{fontenc} \usepackage[english,UKenglish]{babel} \usepackage{mathpazo,booktabs,array,url} \usepackage{\jobname} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \CheckSum{277} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % \GetFileInfo{\jobname.sty} % % \changes{v1.0}{2007/06/11}{Initial public release} % % \DoNotIndex{\@whilenum,\addtocounter,\DeclareRobustCommand,\def} % \DoNotIndex{\do,\edef,\else,\end,\expandafter,\fi,\if,\ifnum} % \DoNotIndex{\if,\let,\NeedsTeXFormat,\newcommand,\newcounter} % \DoNotIndex{\newif,\PackageInfo,\ProvidesPackage} % \DoNotIndex{\RequirePackage,\setcounter,\strlenstore,\the,\value} % \DoNotIndex{\z@} % % \title{From Roman numerals to Arabic numbers} % \author{Joseph Wright% % \thanks{E-mail: joseph.wright@morningstar2.co.uk}} % \date{\filedate} % % \maketitle % % \begin{abstract} % % The |unroman| package provides a number of functions to convert % from Roman numerals to Arabic numbers. The set of functions is % designed to cover a range of applications. % % \end{abstract} % % \section{Introduction} % % TeX and the LaTeX kernel provide a number of functions for % converting Arabic numbers to Roman numerals. However, there is % a lack of functions to go the opposite way, back to Arabic numbers % from a given Roman numeral. The |unroman| package seeks to address % this, providing a flexible set of macros for a variety of % applications. % % \section{Usage} % % \subsection{Conversion from Roman numerals} % % \DescribeMacro{\unroman} % \DescribeMacro{\unromanhead} % \DescribeMacro{\unromantail} % The main aim of the package is to allow Roman numerals to be turned % into Arabic numbers. This is achieved using the % |\unroman|\marg{numeral} macro, which takes a single argument % \meta{numeral}. The case of \meta{numeral} is unimportant, but the % string must only contain valid Roman numeral characters (i, v, x, % c, d, l, m). If the string contains any non-valid characters, % |\unroman| returns the value 0, and writes a message to the log. % The macros |\unromanhead|\marg{numeral} and % |\unromantail|\marg{numeral} extract a Roman numeral from the % beginning and end of a string, respectively. This is illustrated % in Table~\ref{tbl:examples}. % \begin{table} % \centering % \begin{tabular}{ll} % \toprule % LaTeX code & Output \\ % \midrule % |\unroman{MCMXCII}| & \unroman{MCMXCII} \\ % |\unroman{ii--xi}| & \unroman{ii--xi} \\ % |\unromanhead{ii--xi}| & \unromanhead{ii--xi} \\ % |\unromantail{ii--xi}| & \unromantail{ii--xi} \\ % \bottomrule % \end{tabular} % \caption{Conversion examples} % \label{tbl:examples} % \end{table} % % \DescribeMacro{\unromanstore} % \DescribeMacro{\unromanheadstore} % \DescribeMacro{\unromantailstore} % \newcounter{mycounter} % Following the example of the |coolstr| package, versions of all % three conversion macros are provided that do do produce any typeset % output. Instead, they are designed to store the result in a LaTeX % counter for further manipulation. So % |\unromanstore|\marg{numeral}\marg{counter} will store the value % of \meta{numeral} in counter \meta{counter}. For example, % |\unromanstore{mcmxcii}{mycounter}| % \unromanstore{mcmxcii}{mycounter} yields |\the\value{mycounter}| % of \the\value{mycounter}. % % \subsection{The length of a Roman numeral} % % \DescribeMacro{\romanheadlen} % \DescribeMacro{\romantaillen} % \DescribeMacro{\romanheadlenstore} % \DescribeMacro{\romantaillenstore} % Using the core macros of |unroman|, it is easy to design methods % for determining how many characters of a string constitute a valid % Roman numeral. Obviously, if the entire string is expected to be % a numeral, then the |\substr| macro from the |coolstr| package % is applicable. The |unroman| package provides two functions to % measure numerals at the end of strings, |\romanheadlen| and % |\romantaillen|. For example, |\romantaillen{Figure~XI}| % yields \romantaillen{Figure~XI}. Versions of both macros % are provided which store the result in a counter, as % |\romanheadlenstore| and |\romantaillenstore| % % \subsection{Reversing a string} % % \DescribeMacro{\reversestr} % The most efficient method for converting Roman numerals to % Arabic numbers in LaTeX first requires the string to be reversed. % For example, the numeral MCMXCII is first changed to IICXMCM before % processing. As a result, |unroman| contains code to reverse a % string. This is made available as a user command % |\reversestr|\marg{string}. A simple example is % |\reversestr{Hello}|, yielding ``\reversestr{Hello}''. This command is % robust. % % \StopEventually{} % % \iffalse %<*package> % \fi % % \section{Implementation} % % \subsection{Setup code} % % The first part of the package is concerned with basic % identification and loading support packages. The only package % needed is |coolstr|, which is used to provide a length-of-string % function. % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{unroman}% [2007/06/11 v1.0 From Roman numerals to Arabic numbers] \RequirePackage{coolstr} % \end{macrocode} % % \begin{macro}{urm@counta} % \begin{macro}{urm@countb} % \begin{macro}{urm@value} % \begin{macro}{urm@stringlength} % \begin{macro}{\ifurm@tailerror} % Various internal counters are needed by the package in order to % function. These are declared here. A single new Boolean is also % needed. % \begin{macrocode} \newcounter{urm@counta} \newcounter{urm@countb} \newcounter{urm@value} \newcounter{urm@stringlength} \newif\ifurm@tailerror % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Internal macros} % % \begin{macro}{\urm@numtoint} % The business-end macro of the entire package is the conversion of % a single character to its value. Using any of the case-changing % macros (\emph{e.g.}~|\lowercase|, |\MakeLowercase|, \emph{etc.}) % leads to problems. So the function tests for both upper- and % lowercase letters. See also the LaTeX kernel function % |\@slowromancap|, which takes a similar cautious approach to % changing case. % \begin{macrocode} \def\urm@numtoint#1{% \if#1i% 1% \else% \if#1v% 5% \else% \if#1x% 10% \else% \if#1l% 50% \else% \if#1c% 100% \else% \if#1d% 500% \else% \if#1m% 1000% \else% \if#1I% 1% \else \if#1V% 5% \else% \if#1X% 10% \else% \if#1L% 50% \else% \if#1C% 100% \else% \if#1D% 500% \else% \if#1M% 1000% % \end{macrocode} % If the character hasn't been matched, then it is not a valid % Roman numeral. The value $-1$ is used as an indicator of this. % \begin{macrocode} \else% -1% \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi% } % \end{macrocode} % \end{macro} % % \begin{macro}{\urm@calc} % The |\urm@calc| macro is the engine-room of the whole package. % \begin{macrocode} \def\urm@calc#1#2\end{% % \end{macrocode} % First, the current lead character is converted to its integer % equivalent. This is stored in the first working counter % \begin{macrocode} \setcounter{urm@counta}{\urm@numtoint{#1}}% % \end{macrocode} % The value returned by |\urm@numtoint| is examined to check for % the warning value $-1$. % \begin{macrocode} \ifnum\the\value{urm@counta}>\z@% % \end{macrocode} % The character returned is a valid Roman numeral. The next step is % to compare to the value of the previous character. The value is then % compared to that for the previous character (stored in % |urm@countb|). Working from right to left, the current value is % to be taken off the running total if it is less than the previous % value. This is handled by sign inversion. % \begin{macrocode} \ifnum\the\value{urm@counta}<\the\value{urm@countb}% \setcounter{urm@counta}{-\the\value{urm@counta}}% \fi% \addtocounter{urm@value}{\the\value{urm@counta}}% \setcounter{urm@countb}{\the\value{urm@counta}}% % \end{macrocode} % Having carried out the calculation, the string being processed % is shortened by one character, ready for the next pass. % \begin{macrocode} \def\urm@string@a{#2}% % \end{macrocode} % The case where |urm@counta| equals $-1$ value is now handled. % Depending on which user function is in action, processing of the % string may just stop. The alternative is to return a value of 0 % and also provide suitable information in the run log. % \begin{macrocode} \else% \ifurm@tailerror% \PackageInfo{unroman}% {Invalid character in string --- returning value 0}% \setcounter{urm@value}{0}% \fi% \def\urm@string@a{}% \fi% } % \end{macrocode} % \end{macro} % % \begin{macro}{\urm@reverse@string} % The core of the string-reversing algorithm is picking off the % first character of the string and using it to build a new string, % in the opposite direction. Here, |\urm@string@b| is used to % store the new string, while |\urm@string@c| holds the original % information being ``eaten'' at each step. % \begin{macrocode} \def\urm@reverse@string#1#2\end{% \edef\urm@string@b{#1\urm@string@b}% \def\urm@string@c{#2}% } % \end{macrocode} % \end{macro} % \begin{macro}{\urm@reversestring} % The second internal function for reversing strings loops through % each character of the supplied string. As this function is used % for internal reasons, it doesn't print anything. This leads to % the need for this to be an internal function. % \begin{macrocode} \def\urm@reversestring#1{% \def\urm@string@c{#1}% \strlenstore{#1}{urm@stringlength}% \def\urm@string@b{}% \@whilenum\the\value{urm@stringlength}>\z@\do% {\expandafter\urm@reverse@string\urm@string@c\end% \strlenstore{\urm@string@c}{urm@stringlength}% }% } % \end{macrocode} % \end{macro} % % \begin{macro}{\urm@roman@length} % The core function for finding the length of a Roman numeral % uses |\urm@numtoint| to determine whether characters in the % string are valid Roman numerals. Once this stops, the % string is truncated. % \begin{macrocode} \def\urm@roman@length#1#2\end{% \setcounter{urm@counta}{\urm@numtoint{#1}}% \ifnum\the\value{urm@counta}>\z@% \addtocounter{urm@value}{1}% \def\urm@string@a{#2}% \else% \def\urm@string@a{}% \fi% } % \end{macrocode} % \end{macro} % \begin{macro}{\urm@romanlength} % The counter for the length of a Roman numeral is set up here, and % the same looping as used elsewhere is implemented. % \begin{macrocode} \def\urm@romanlength#1{% \setcounter{urm@value}{0}% \strlenstore{#1}{urm@stringlength}% \def\urm@string@a{#1}% \@whilenum\the\value{urm@stringlength}>\z@\do% {\expandafter\urm@roman@length\urm@string@a\end% \strlenstore{\urm@string@a}{urm@stringlength}% }% } % \end{macrocode} % \end{macro} % % \begin{macro}{\urm@make@head} % \begin{macro}{\urm@makehead} % The functions to strip a Roman numeral at the start of a string % are very similar to those used to count how long such a string % is. The only difference is the construction of a new string as % the counting occurs. % \begin{macrocode} \def\urm@make@head#1#2\end{% \setcounter{urm@counta}{\urm@numtoint{#1}}% \ifnum\the\value{urm@counta}>\z@% \edef\urm@string@b{\urm@string@b#1}% \def\urm@string@a{#2}% \else% \def\urm@string@a{}% \fi% } \def\urm@makehead#1{% \strlenstore{#1}{urm@stringlength}% \def\urm@string@a{#1}% \def\urm@string@b{}% \@whilenum\the\value{urm@stringlength}>\z@\do% {\expandafter\urm@make@head\urm@string@a\end% \strlenstore{\urm@string@a}{urm@stringlength}% }% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\urm@unroman} % The final internal macro is the loop to convert a Roman numeral % into a number. % \begin{macrocode} \newcommand*{\urm@unroman}[1]{% % \end{macrocode} % The counters needed are initialised. The counter |urm@value| is % used to hold the result of the conversion, whilst |urm@countb| % is needed to track the value of the previously-converted character. % \begin{macrocode} \setcounter{urm@value}{0}% \setcounter{urm@countb}{0}% % \end{macrocode} % The string is reversed, as this makes it much easier to calculate % the value of the numeral. % \begin{macrocode} \urm@reversestring{#1}% \let\urm@string@a\urm@string@b% % \end{macrocode} % The main loop then converts the value, iterating through the string % one character at a time. % \begin{macrocode} \strlenstore{\urm@string@a}{urm@stringlength}% \@whilenum\the\value{urm@stringlength}>\z@\do% {\expandafter\urm@calc\urm@string@a\end% \strlenstore{\urm@string@a}{urm@stringlength}% }% } % \end{macrocode} % \end{macro} % % \subsection{User space macros} % % \begin{macro}{\reversestr} % The string reversing macro simply calls the internal version of % the function, then outputs the resulting string. Due to the % interaction of the internal macros, this ends up as % |\urm@string@b| rather than the more obvious |\urm@string@a|. % \begin{macrocode} \DeclareRobustCommand*{\reversestr}[1]{% \urm@reversestring{#1}% \urm@string@b% } % \end{macrocode} % \end{macro} % % \begin{macro}{\unroman} % \begin{macro}{\unromanstore} % \begin{macro}{\unromantail} % \begin{macro}{\unromantailstore} % \begin{macro}{\unromanhead} % \begin{macro}{\unromanheadstore} % \begin{macro}{\unromanheadlen} % \begin{macro}{\unromanheadlenstore} % \begin{macro}{\unromantaillen} % \begin{macro}{\unromantaillenstore} % All of the user space functions to generate numbers come in two % versions. The |store| versions are modelled on the |coolstr| % package. All of these functions simple make appropriate calls to % the internal functions, then deal correctly with the output. % \begin{macrocode} \newcommand*{\unroman}[1]{% \urm@tailerrortrue% \urm@unroman{#1}% \the\value{urm@value}% } \newcommand*{\unromanstore}[2]{% \urm@tailerrortrue% \urm@unroman{#1}% \setcounter{#2}{\the\value{urm@value}}% } \newcommand*{\unromantail}[1]{% \urm@tailerrorfalse% \urm@unroman{#1}% \the\value{urm@value}% } \newcommand*{\unromantailstore}[2]{% \urm@tailerrorfalse% \urm@unroman{#1}% \setcounter{#2}{\the\value{urm@value}}% } \newcommand*{\unromanhead}[1]{% \urm@makehead{#1}% \expandafter\unroman\expandafter{\urm@string@b}% } \newcommand*{\unromanheadstore}[2]{% \urm@makehead{#1}% \expandafter\unromanstore\expandafter{\urm@string@b}{#2}% } \newcommand*{\romanheadlen}[1]{% \urm@romanlength{#1}% \the\value{urm@value}% } \newcommand*{\romanheadlenstore}[2]{% \urm@romanlength{#1}% \setcounter{#2}{\the\value{urm@value}}% } \newcommand*{\romantaillen}[1]{% \urm@reversestring{#1}% \expandafter\urm@romanlength\expandafter{\urm@string@b}% \the\value{urm@value}% } \newcommand*{\romantaillenstore}[2]{% \urm@reversestring{#1}% \expandafter\urm@romanlength\expandafter{\urm@string@b}% \setcounter{#2}{\the\value{urm@value}}% } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \iffalse % % \fi % \PrintChanges % \PrintIndex % \Finale