%D \module
%D   [       file=core-con,
%D        version=1997.26.08,
%D          title=\CONTEXT\ Core Macros,
%D       subtitle=Conversion Macros,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA / Hans Hagen \& Ton Otten}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\writestatus{loading}{Context Core Macros / Conversion Macros}

\unprotect

%D This module deals with all kind of conversions from numbers
%D and dates. I considered splitting this module in a support
%D one and a core one, but to keep things simple as well as
%D preserve the overview, I decided against splitting.

%D \macros
%D   {numbers}
%D
%D First we deal with the dummy conversion of numbers using the
%D \TEX\ primitive \type{\number}. The uppercase alternative is
%D only there for compatibility with the other conversion
%D macros. We could do without \type{#1} but this way we get
%D rid of unwanted braces. For the savety we also define a
%D non||sence uppercase alternative.
%D
%D \showsetup{\y!numbers}

\def\numbers#1{\number#1}
\def\Numbers#1{\number#1}

%D \macros
%D   {romannumerals,Romannumerals}
%D
%D \TEX\ the program uses a rather tricky conversion from
%D numbers to their roman counterparts. This conversion could
%D of course be programmed in \TEX\ itself, but I guess Knuth
%D found the programming trick worth presenting.
%D
%D \showsetup{\y!romannumerals}
%D \showsetup{\y!Romannumerals}
%D
%D When upcasing the result, we just follow the text book rules
%D of expansion. Later on we'll see some more uppercase tricks.

\def\romannumerals#1%
  {\romannumeral#1}

%D For some years we had \unknown
%D
%D \starttypen 
%D \def\Romannumerals#1%
%D   {\uppercase\expandafter{\romannumeral#1}}
%D \stoptypen 
%D 
%D \unknown but we need to be fully expandable in order to get
%D the utility output file right, so now we have the following
%D solution. It was Patrick Gundlach who first noticed this
%D ommision. 
 
\def\Romannumerals#1%
  {\expandafter\doRomannumerals\number#1\relax}

\def\doRomannumerals#1#2\relax % spaces after ifcase prevent \relax
  {\ifnum#1#2<10
     \ifcase0#1#2 \or I\or II\or III\or IV\or V\or VI\or VII\or VIII\or IX\fi
   \else\ifnum#1#2<100
     \ifcase0#1   \or X\or XX\or XXX\or XL\or L\or LX\or LXX\or LXXX\or XC\fi 
     \doRomannumerals#2\relax 
   \else\ifnum#1#2<1000
     \ifcase0#1   \or C\or CC\or CCC\or CD\or D\or DC\or DCC\or DCCC\or CM\fi 
     \doRomannumerals#2\relax 
   \else\ifnum#1#2<4000
     \ifcase0#1   \or M\or MM\or MMM\fi
     \doRomannumerals#2\relax 
   \else
     \uppercase\expandafter{\romannumeral#1#2}%
   \fi\fi\fi\fi}

%D \macros
%D   {character,Character}
%D
%D Converting a number into a character can of course only
%D be done with numbers less or equal to~26. At the cost of
%D much more macros a faster conversion is possible, using:
%D
%D \starttypen
%D \setvalue{char1}{a} \def\character#1{\getvalue{char#1}}
%D \stoptypen
%D
%D But we prefer a simpel \type{\case}.
%D
%D \showsetup{\y!character}
%D \showsetup{\y!Character}

\def\unknowncharacter{-} % else in lists \relax

\def\character#1%
  {\ifcase#1\unknowncharacter
     \or a\or b\or c\or d\or e\or f\or g\or h\or i\or j\or k\or l\or m%
     \or n\or o\or p\or q\or r\or s\or t\or u\or v\or w\or x\or y\or z%
   \else
     \unknowncharacter
   \fi}

\def\Character#1%
  {\ifcase#1\unknowncharacter
     \or A\or B\or C\or D\or E\or F\or G\or H\or I\or J\or K\or L\or M%
     \or N\or O\or P\or Q\or R\or S\or T\or U\or V\or W\or X\or Y\or Z%
   \else
     \unknowncharacter
   \fi}

%D \macros
%D   {characters,Characters}
%D
%D Converting large numbers is supported by the next two
%D macros. This time we just count on: $\cdots$~x, y, z, aa,
%D ab, ac~$\cdots$.
%D
%D \showsetup{\y!characters}
%D \showsetup{\y!Characters}

\def\doconvertcharacters#1#2%
  {\ifnum#2>26
     \bgroup
     \!!counta#2
     \ifnum\!!counta>\zerocount
       \advance\!!counta \minusone
       \!!countb\!!counta
       \divide\!!counta 26
       \!!countc\!!counta
       \multiply\!!countc 26
       \advance\!!countb -\!!countc
       \doconvertcharacters#1{\!!counta}%
       \advance\!!countb \plusone
       #1{\the\!!countb}%
     \fi
     \egroup
   \else
     #1{#2}% pure expansion, used in references
   \fi}

\def\characters{\doconvertcharacters\character}
\def\Characters{\doconvertcharacters\Character}

%D \macros
%D   {greeknumerals,Greeknumerals}
%D
%D Why should we only honour the romans, and not the greek?

\def\greeknumerals#1% watch the \normalxi
   {\mathematics
      {\ifcase#1\unknowncharacter\or
         \alpha\or\beta\or\gamma\or\delta\or\varepsilon\or
         \zeta\or\eta\or\theta\or\iota\or\kappa\or\lambda\or
         \mu\or\nu\or\normalxi\or o\or\pi\or\varrho\or\sigma\or
         \tau\or\upsilon\or\phi\or\chi\or\psi\or\omega
       \else
         \unknowncharacter
       \fi}}

\def\Greeknumerals#1%
  {\mathematics
     {\ifcase#1\unknowncharacter \or
        A\or B\or\Gamma\or\Delta\or E\or Z\or H\or\Theta\or
        I\or K\or\Lambda\or M\or N\or\Xi\or O\or\Pi\or P\or
        \Sigma\or T\or\Upsilon\or\Phi\or X\or\Psi\or\Omega
      \else
        \unknowncharacter
      \fi}}

%D \macros
%D   {oldstylenumerals,oldstyleromannumerals}
%D
%D These conversions are dedicated to Frans Goddijn. 

\unexpanded\def\oldstylenumerals#1%
  {{\os\number#1}}

\unexpanded\def\oldstyleromannumerals#1%
  {{\leftrulefalse\rightrulefalse\ss\txx\boxrulewidth.15ex
    \ruledhbox spread .15em{\hss\uppercased{\romannumerals{#1}}\hss}}}

%D \macros
%D   {protectconversion}
%D
%D The previous two commands are not robust enough to be
%D passed to \type{\write} en \type{\message}. That's why we
%D introduce:

\def\protectconversion
  {\def\doconvertcharacters##1{##1}} % was \relax
 %{\def\doconvertcharacters##1{\ifcase0##1 0\else##1\fi}} more save

%D \macros
%D   {normaltime,normalyear,normalmonth,normalday}
%D
%D The last part of this module is dedicated to converting
%D dates. Because we want to use as meaningful commands as
%D possible, and because \TEX\ already uses up some of those,
%D we save the original meanings.

\savenormalmeaning\time  
\savenormalmeaning\year  
\savenormalmeaning\month 
\savenormalmeaning\day   

%D \macros
%D   {month,MONTH}
%D
%D Converting the month number into a month name is done
%D using a case statement, abstact values and the label
%D mechanism. This way users can easily redefine a label from
%D for instance german into austrian.
%D
%D \starttypen
%D \setuplabeltext [de] [january=J\"anner]
%D \stoptypen
%D
%D Anyhow, the conversion looks like:

\def\doconvertmonth#1%
  {\labeltext
     {\ifcase#1%
      \or \v!january   \or \v!february \or \v!march    \or \v!april
      \or \v!may       \or \v!june     \or \v!july     \or \v!august
      \or \v!september \or \v!october  \or \v!november \or \v!december
      \fi}}

%D We redefine the \TEX\ primitive \type{\month} as:
%D
%D \showsetup{\y!month}
%D \showsetup{\y!MONTH}

\def\month
  {\doconvertmonth}

\def\MONTH#1%
  {{\let\labeltext\LABELTEXT\month{#1}}}

%D We never explicitly needed this, but Tobias Burnus pointed
%D out that it would be handy to convert to the day of the
%D week. In doing so, we have to calculate the total number of
%D days, taking leapyears into account. For those who are
%D curious:
%D
%D \startopsomming[opelkaar]
%D \som  years that can be divided by 4 are leapyears
%D \som  exept years that can be divided by 100
%D \som  unless years can be divided by 400
%D \stopopsomming
%D
%D This makes the year 1900 into a normal year and 1996 and
%D 2000 into leap years, right? Well, converting to string
%D looks familiar:

\def\doconvertday#1%
  {\labeltext
     {\ifcase#1
      \or \v!sunday   \or \v!monday \or \v!tuesday  \or \v!wednesday
      \or \v!thursday \or \v!friday \or \v!saturday \fi}}

%D \macros
%D   {getdayoftheweek, dayoftheweek}
%D
%D The conversion algoritm is an old one and a translation from
%D a procedure written in MODULA~2 back in the 80's. I finaly
%D found the 4--100-400 rules in some enclopedia. Look at this
%D messy low level routine that takes the day, month and year
%D as arguments:

\newcount\normalweekday

\def\getdayoftheweek#1#2#3%
  {\bgroup
   \!!counta#3\relax 
   \advance\!!counta \minusone
   \!!countb\!!counta 
   \multiply\!!countb 365
   \advance\!!countb \ifcase#2\relax
     0 \or  0 \or 31 \or 59 \or 90 \or120 \or151 \or
   181 \or212 \or243 \or273 \or304 \or334 \or365 \fi
   \advance\!!countb #1\relax
   \ifnum#2>2
     \doifleapyearelse{#3}{\advance\!!countb 1}{}\relax
   \fi
   \!!countc\!!counta 
%  \DoDiv\!!countc by4to\!!countc
   \dosetdivision\!!countc4\!!countc
   \advance\!!countb \!!countc
   \!!countc\!!counta 
%  \DoDiv\!!countc by100to\!!countc
   \dosetdivision\!!countc{100}\!!countc
   \advance\!!countb -\!!countc
   \!!countc\!!counta 
%  \DoDiv\!!countc by400to\!!countc
   \dosetdivision\!!countc{400}\!!countc
   \advance\!!countb \!!countc
%  \DoMod\!!countb by7to\!!countb
   \dosetmodulo\!!countb7\!!countb
   \advance\!!countb \plusone 
   \@EA\egroup\@EA\normalweekday\the\!!countb\relax}

\def\dayoftheweek#1#2#3%
  {\getdayoftheweek{#1}{#2}{#3}\doconvertday{\normalweekday}}

%D Using this macro in
%D
%D \startbuffer
%D monday:   \dayoftheweek  {4} {5} {1992}
%D friday:   \dayoftheweek {16} {6} {1995}
%D monday:   \dayoftheweek {25} {8} {1997}
%D saturday: \dayoftheweek {30} {8} {1997}
%D tuesday:  \dayoftheweek  {2} {1} {1996}
%D tuesday:  \dayoftheweek  {7} {1} {1997}
%D tuesday:  \dayoftheweek {13} {1} {1998}
%D friday:   \dayoftheweek  {1} {1} {2000}
%D \stopbuffer
%D
%D \typebuffer
%D
%D gives
%D
%D \startvoorbeeld
%D \startregels
%D \haalbuffer
%D \stopregels
%D \stopvoorbeeld
%D
%D The macro \type {\getdayoftheweek} can be used to calculate
%D the number \type {\normalweekday}.

%D \macros
%D   {weekday,WEEKDAY}
%D
%D The first one is sort of redundant. It takes the day
%D number argument.
%D
%D \showsetup{\y!weekday}
%D \showsetup{\y!WEEKDAY}

\def\weekday
  {\doconvertday}

\def\WEEKDAY#1%
  {{\let\labeltext\LABELTEXT\doconvertday{#1}}}

%D \macros
%D   {weekoftheday}
%D
%D {\em not yet implemented:}
%D
%D \starttypen
%D \def\weekoftheday#1#2#3%
%D  {}
%D \stoptypen

%D \macros
%D   {doifleapyearelse,
%D    getdayspermonth}
%D
%D Sometimes we need to know if we're dealing with a
%D leapyear, so here is a testmacro:
%D
%D \starttypen
%D \doifleapyearelse{year}{yes}{no}
%D \stoptypen
%D
%D An example of its use can be seen in the macro
%D
%D \starttypen
%D \getdayspermonth{year}{month}
%D \stoptypen
%D
%D The number of days is available in the macro \type
%D {\numberofdays}.

\def\doifleapyearelse#1% #2#3%
  {\bgroup
   \!!doneafalse
   \!!counta#1%
%  \DoMod\!!counta by4to\!!countb
   \dosetmodulo\!!counta4\!!countb
   \ifcase\!!countb
%    \DoMod\!!counta by100to\!!countb
     \dosetmodulo\!!counta{100}\!!countb
     \ifcase\!!countb \else \!!doneatrue \fi
%    \DoMod\!!counta by400to\!!countb
     \dosetmodulo\!!counta{400}\!!countb
     \ifcase\!!countb \!!doneatrue \fi
   \fi
   \if!!donea
     \egroup\@EA\firstoftwoarguments  % \def\next{#2}%
   \else
     \egroup\@EA\secondoftwoarguments % \def\next{#3}%
   \fi}                               % \next}

\def\getdayspermonth#1#2%
  {\doifleapyearelse{#1}
     {\def\numberofdays{29}}
     {\def\numberofdays{28}}%
   \edef\numberofdays%
     {\ifcase#2 \or31\or\numberofdays\or31\or30\or
        31\or30\or31\or31\or30\or31\or30\or31\fi}}

%D \macros
%D   {currentdate, date}
%D
%D We use these conversion macros in the date formatting
%D macro:
%D
%D \showsetup{\y!currentdate}
%D
%D This macro takes care of proper spacing and delivers for
%D instance:
%D
%D \startbuffer
%D \currentdate[weekdag,dag,maand,jaar] % still dutch example
%D \currentdate[WEEKDAG,dag,MAAND,jaar] % still dutch example
%D \stopbuffer
%D
%D \startvoorbeeld
%D \startregels
%D \haalbuffer
%D \stopregels
%D \stopvoorbeeld
%D
%D depending of course on the keywords. Here we gave:
%D
%D \typebuffer
%D
%D If needed one can also add non||keywords, like in
%D
%D \startbuffer
%D \currentdate[dd,--,mm,--,yy]
%D \stopbuffer
%D
%D \typebuffer
%D
%D or typeset: \haalbuffer.
%D
%D When no argument is passed, the current date is given as
%D specified per language (using \type{\installlanguage}).
%D
%D \showsetup{\y!currentdate}
%D
%D \startbuffer
%D \date
%D \date[d=12,m=12,y=1998][weekdag]
%D \date[d=12,m=12,y=1998]
%D \stopbuffer
%D
%D We can also typeset arbitrary dates, using the previous
%D command.
%D
%D \typebuffer
%D
%D The date is specified by one character keys. When no date
%D is given, we get the current date.
%D
%D \startregels
%D \haalbuffer
%D \stopregels

\def\kenmerkdatumpatroon{j,mm,dd} % jj,mm,dd changed at januari 1-1-2000 

\newsignal\datesignal

\def\dobetweendates
  {\ifdim\lastskip=\datesignal\relax\else
     \unskip\space
     \hskip\datesignal\relax
   \fi}

% \def\complexcurrentdate[#1]%
%   {\bgroup
%    \let\labellanguage\currentlanguage
%    \def\betweendates{\let\betweendates\dobetweendates}%
%    \lowercase{\edef\!!stringa{#1}}% permits usage in \kap 
%    \@EA\processallactionsinset\@EA
%      [\!!stringa]
%      [    \v!dag=>\betweendates\the\normalday,
%         \v!maand=>\betweendates\month\normalmonth,
%          \v!jaar=>\betweendates\the\normalyear,
%           \space=>\unskip\ \hskip\datesignal,% optimization -) 
%               \ =>\unskip\ \hskip\datesignal,% optimization -) 
%                d=>\the\normalday,
%                m=>\the\normalmonth,
%                j=>\the\normalyear,
%                y=>\the\normalyear,
%                w=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear,
%               dd=>\ifnum\normalday  >9 \else0\fi\the\normalday,
%               mm=>\ifnum\normalmonth>9 \else0\fi\the\normalmonth,
%               jj=>\expandafter\gobbletwoarguments\the\normalyear,
%               yy=>\expandafter\gobbletwoarguments\the\normalyear,
%       \v!weekdag=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear,
%       \v!kenmerk=>\expanded{\complexcurrentdate[\kenmerkdatumpatroon]},
%       \s!unknown=>\unskip
%                   \commalistelement
%                   \hskip\datesignal
%                   \def\betweendates{\let\betweendates\dobetweendates}]%
%    \ifdim\lastskip=\datesignal\relax
%      \unskip
%    \fi
%    \egroup}

% \def\complexcurrentdate[#1]%
%   {\bgroup
%    \let\labellanguage\currentlanguage
%    \def\betweendates{\let\betweendates\dobetweendates}%
%    \lowercase{\edef\!!stringa{#1}}% permits usage in \kap 
%    \@EA\processallactionsinset\@EA
%      [\!!stringa]
%      [    \v!dag=>\betweendates\the\normalday,
%          \v!dag+=>\betweendates\ordinaldaynumber\normalday,
%         \v!maand=>\betweendates\month\normalmonth,
%          \v!jaar=>\betweendates\the\normalyear,
%        \v!spatie=>\unskip\ \hskip\datesignal,% optimization -) 
%               \ =>\unskip\ \hskip\datesignal,% optimization -) 
%                d=>\the\normalday,
%               d+=>\ordinaldaynumber\normalday,
%                m=>\the\normalmonth,
%                j=>\the\normalyear,
%                y=>\the\normalyear,
%                w=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear,
%               dd=>\ifnum\normalday  >9 \else0\fi\the\normalday,
%              dd+=>\ordinaldaynumber{\ifnum\normalday  >9 \else0\fi\the\normalday},
%               mm=>\ifnum\normalmonth>9 \else0\fi\the\normalmonth,
%               jj=>\expandafter\gobbletwoarguments\the\normalyear,
%               yy=>\expandafter\gobbletwoarguments\the\normalyear,
%       \v!weekdag=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear,
%       \v!kenmerk=>\expanded{\complexcurrentdate[\kenmerkdatumpatroon]},
%       \s!unknown=>\unskip
%                   \commalistelement
%                   \hskip\datesignal
%                   \def\betweendates{\let\betweendates\dobetweendates}]%
%    \ifdim\lastskip=\datesignal\relax
%      \unskip
%    \fi
%    \egroup}

\def\complexcurrentdate[#1]%
  {\bgroup
   \let\labellanguage\currentlanguage
   \def\betweendates{\let\betweendates\dobetweendates}%
   \lowercase{\edef\!!stringa{#1}}% permits usage in \kap 
   \@EA\processallactionsinset\@EA
     [\!!stringa]
     [    \v!dag=>\betweendates\the\normalday,
        %\v!dag+=>\betweendates\ordinaldaynumber\normalday,
         \v!dag+=>\betweendates\convertnumber{\v!dag+}\normalday,
        \v!maand=>\betweendates\month\normalmonth,
         \v!jaar=>\betweendates\the\normalyear,
       \v!spatie=>\unskip\ \hskip\datesignal,% optimization -) 
              \ =>\unskip\ \hskip\datesignal,% optimization -) 
               d=>\convertnumber\v!dag\normalday,
             %d+=>\ordinaldaynumber\normalday,
              d+=>\convertnumber{\v!dag+}\normalday,
               m=>\convertnumber\v!maand\normalmonth,
               j=>\convertnumber\v!jaar\normalyear,
               y=>\convertnumber\v!jaar\normalyear,
               w=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear,
              dd=>\ifnum\normalday  >9 \else0\fi\the\normalday,
            %dd+=>\ordinaldaynumber{\ifnum\normalday >9 \else0\fi\the\normalday},
             dd+=>\convertnumber{\v!dag+}{\ifnum\normalday >9 \else0\fi\the\normalday},
              mm=>\ifnum\normalmonth>9 \else0\fi\the\normalmonth,
              jj=>\expandafter\gobbletwoarguments\the\normalyear,
              yy=>\expandafter\gobbletwoarguments\the\normalyear,
      \v!weekdag=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear,
      \v!kenmerk=>\expanded{\complexcurrentdate[\kenmerkdatumpatroon]},
      \s!unknown=>\unskip
                  \commalistelement
                  \hskip\datesignal
                  \def\betweendates{\let\betweendates\dobetweendates}]%
   \ifdim\lastskip=\datesignal\relax
     \unskip
   \fi
   \egroup}

\def\simplecurrentdate
  {\expanded{\complexcurrentdate[\currentdatespecification]}}

\definecomplexorsimple\currentdate

\def\dodate[#1][#2]%
  {\bgroup
   \iffirstargument
     \getparameters[\??da][d=\normalday,m=\normalmonth,y=\normalyear,#1]%
     \normalday  \@@dad\relax
     \normalmonth\@@dam\relax
     \normalyear \@@day\relax
     \ifsecondargument
       \currentdate[#2]%
     \else
       \currentdate
     \fi
   \else
     \currentdate
   \fi
   \egroup}

\def\date
  {\dodoubleempty\dodate}

%D \macros
%D   {currenttime}
%D
%D The currenttime is actually the jobtime. You can specify 
%D a pattern similar to the previous date macro using the 
%D keys \type {h}, \type {m} and a separator.  

\def\calculatecurrenttime
% {\DoDiv\time by60to\scratchcounter\edef\currenthour  {\the\scratchcounter}%
%  \DoMod\time by60to\scratchcounter\edef\currentminute{\the\scratchcounter}}
  {\dosetdivision\time{60}\scratchcounter
   \edef\currenthour  {\ifnum\scratchcounter<10 0\fi \the\scratchcounter}%
   \dosetmodulo  \time{60}\scratchcounter
   \edef\currentminute{\ifnum\scratchcounter<10 0\fi \the\scratchcounter}}

\appendtoks \calculatecurrenttime \to \everyjob

\def\currenttimespecification{h,:,m}

\def\complexcurrenttime[#1]%
  {\calculatecurrenttime
   \processallactionsinset[#1]
     [h=>\currenthour,m=>\currentminute,\s!unknown=>\commalistelement]}

\def\simplecurrenttime
  {\expanded{\complexcurrenttime[\currenttimespecification]}}

\definecomplexorsimple\currenttime
   
%D Because we're dealing with dates, we also introduce a few
%D day loops:
%D
%D \starttypen
%D \processmonth{year}{month}{command}
%D \processyear{year}{command}{before}{after}
%D \stoptypen
%D
%D The counters \type {\normalyear}, \type {\normalmonth} and
%D \type{\normalday} can be used for for date manipulations.

\long\def\processmonth#1#2#3% year month command
  {\bgroup
   \getdayspermonth{#1}{#2}%
   \dostepwiserecurse1\numberofdays1%
     {\normalyear #1\relax
      \normalmonth#2\relax
      \normalday  \recurselevel\relax
      #3}%
   \egroup}

\def\lastmonth{12} % can be set to e.g. 1 when testing

\long\def\processyear#1#2#3#4% year command before after
  {\bgroup
   \dorecurse\lastmonth
     {\normalyear #1\relax
      \normalmonth\recurselevel\relax
      #3\processmonth\normalyear\normalmonth{#2}#4}%
   \egroup}

%D \macros
%D   {defineconversion, convertnumber}
%D
%D Conversion involves the macros that we implemented earlier
%D in this module.
%D
%D \showsetup{\y!defineconversion}
%D \showsetup{\y!convertnumber}
%D
%D We can feed this command with conversion macros as well as
%D a set of conversion symbols. Both need a bit different
%D treatment.
%D
%D \starttypen
%D \defineconversion [roman] [\romannumerals]
%D \defineconversion [set 1] [$\star$,$\bullet$,$\ast$]
%D \stoptypen
%D
%D You can define a language dependent conversion with: 
%D
%D \starttypen
%D \defineconversion [en] [whatever] [\something]
%D \stoptypen

% \def\dodefineconversion[#1][#2]%
%   {\ConvertConstantAfter\doifinstringelse{,}{#2}
%      {\scratchcounter=0
%       \def\docommando##1%
%         {\advance\scratchcounter 1
%          \setvalue{\??cv#1\the\scratchcounter}{##1}}%
%       \processcommalist[#2]\docommando
%       \setvalue{\??cv#1}##1{\csname\??cv#1##1\endcsname}}
%      {\setvalue{\??cv#1}{#2}}}
% 
% \def\defineconversion%
%   {\dodoubleargument\dodefineconversion}

\def\defineconversion
  {\dotripleempty\dodefineconversion}

\def\dodefineconversion[#1][#2][#3]%
  {\ifthirdargument
     \dododefineconversion[#1][#2][#3]%
   \else
     \dododefineconversion[][#1][#2]%
   \fi}

\def\dododefineconversion[#1][#2][#3]%
  {\ConvertConstantAfter\doifinstringelse{,}{#3}
     {\scratchcounter\zerocount
      \def\docommando##1%
        {\advance\scratchcounter \plusone
         \setvalue{\??cv#1#2\the\scratchcounter}{##1}}%
      \processcommalist[#3]\docommando
      \setvalue{\??cv#1#2}##1{\csname\??cv#1#2##1\endcsname}}
     {\setvalue{\??cv#1#2}{#3}}}

% \def\convertnumber#1%
%   {\csname\??cv#1\endcsname}

% \def\convertnumber#1%
%   {\csname\??cv
%      \ifundefined{\??cv\currentlanguage#1}\else\currentlanguage\fi
%    #1\endcsname}

% \def\doifconversiondefinedelse#1%
%   {\ifundefined{\??cv#1}%
%      \@EA\secondoftwoarguments
%    \else
%      \@EA\firstoftwoarguments
%    \fi}

% \def\doifconversiondefinedelse#1%
%   {\ifundefined{\??cv\ifundefined{\??cv\currentlanguage:#1}\else\currentlanguage\fi#1}%
%      \@EA\secondoftwoarguments
%    \else
%      \@EA\firstoftwoarguments
%    \fi}
 
\beginETEX \ifcsname

\def\convertnumber#1%
  {\csname\??cv
     \ifcsname\??cv\currentlanguage#1\endcsname
       \currentlanguage#1%
     \else\ifcsname\??cv#1\endcsname
       #1%
     \else
       \s!default
     \fi\fi
   \endcsname}

\def\doifconversiondefinedelse#1%
  {\ifcsname\??cv\currentlanguage#1\endcsname
     \@EA\firstoftwoarguments
   \else\ifcsname\??cv#1\endcsname
     \@EAEAEA\firstoftwoarguments
   \else
     \@EAEAEA\secondoftwoarguments
   \fi\fi}
 
\endETEX

\beginTEX

\def\convertnumber#1%
  {\csname\??cv
     \@EA\ifx\csname\??cv\currentlanguage#1\endcsname\relax
       \@EA\ifx\csname\??cv#1\endcsname\relax
         \s!default
       \else
         #1%
       \fi
     \else
       \currentlanguage#1%
     \fi
   \endcsname}

\def\doifconversiondefinedelse#1%
  {\@EA\ifx\csname\??cv\currentlanguage#1\endcsname\relax
     \@EA\ifx\csname\??cv#1\endcsname\relax
       \@EAEAEA\secondoftwoarguments
     \else
       \@EAEAEA\firstoftwoarguments
     \fi
   \else
     \@EA\firstoftwoarguments
   \fi}

\endTEX

\defineconversion [\s!default] [\number]

%D As longs as symbols are linked to levels or numbers, we can
%D also use the conversion mechanism, but in for instance the
%D itemization macros, we prefer symbols because they can more
%D easier be (partially) redefined. Symbols are implemented
%D in another module.

\defineconversion []   [\numbers] % the default conversion

\defineconversion [a]  [\characters]
\defineconversion [A]  [\Characters]
\defineconversion [AK] [\kap\characters]
\defineconversion [KA] [\kap\characters]

\defineconversion [n]  [\numbers]
\defineconversion [N]  [\Numbers]
\defineconversion [m]  [\mediaeval]

\defineconversion [r]  [\romannumerals]
\defineconversion [R]  [\Romannumerals]
\defineconversion [KR] [\kap\romannumerals]
\defineconversion [RK] [\kap\romannumerals]

\defineconversion [g]  [\greeknumerals]
\defineconversion [G]  [\Greeknumerals]

\defineconversion [o]  [\oldstylenumerals]
\defineconversion [or] [\oldstyleromannumerals]

\defineconversion [\v!letter]    [\character]
\defineconversion [\v!Letter]    [\Character]

\defineconversion [\v!letters]   [\characters]
\defineconversion [\v!Letters]   [\Characters]

\defineconversion [\v!cijfers]   [\numbers]
\defineconversion [\v!Cijfers]   [\Numbers]
\defineconversion [\v!mediaeval] [\mediaeval]

\defineconversion [\v!romeins]   [\romannumerals]
\defineconversion [\v!Romeins]   [\Romannumerals]

\defineconversion [\v!grieks]    [\greeknumerals]
\defineconversion [\v!Grieks]    [\Greeknumerals]

% Some bonus ones:

\defineconversion [\v!leeg]      [\gobbleoneargument]
\defineconversion [\v!geen]      [\numbers]

\defineconversion
  [set 0]
  [{\symbol[bullet]},      
   {\symbol[dash]},
   {\symbol[star]},  
   {\symbol[triangle]},
   {\symbol[circle]}, 
   {\symbol[medcircle]},
   {\symbol[bigcircle]},    
   {\symbol[square]}]

\defineconversion
  [set 1]
  [\mathematics{\star},            
   \mathematics{\star\star},
   \mathematics{\star\star\star},  
   \mathematics{\ddagger}, 
   \mathematics{\ddagger\ddagger}, 
   \mathematics{\ddagger\ddagger\ddagger},
   \mathematics{\ast},             
   \mathematics{\ast\ast},
   \mathematics{\ast\ast\ast}]

\defineconversion
  [set 2]
  [\mathematics{*},
   \mathematics{\dag},
   \mathematics{\ddag},
   \mathematics{**},
   \mathematics{\dag\dag},
   \mathematics{\ddag\ddag},
   \mathematics{***},
   \mathematics{\dag\dag\dag},
   \mathematics{\ddag\ddag\ddag},
   \mathematics{****},
   \mathematics{\dag\dag\dag\dag},
   \mathematics{\ddag\ddag\ddag\ddag}]

\defineconversion
  [set 3]
  [\mathematics{\star},
   \mathematics{\star\star},
   \mathematics{\star\star\star},
   \mathematics{\ddagger},
   \mathematics{\ddagger\ddagger},
   \mathematics{\ddagger\ddagger\ddagger},
   \mathematics{\P},
   \mathematics{\P\P},
   \mathematics{\P\P\P},
   \mathematics{\S},   
   \mathematics{\S\S},
   \mathematics{\S\S\S},
   \mathematics{\ast},
   \mathematics{\ast\ast},
   \mathematics{\ast\ast\ast}]

\protect \endinput
