% \iffalse meta-comment
%
% Copyright (C) 2007 by
%    Joseph Wright <joseph.wright@morningstar2.co.uk>
%
% 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}
%</driver>
% \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
%</package>
% \fi
% \PrintChanges
% \PrintIndex
% \Finale
