%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. Non||commercial use is
%C granted.

\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}

\def\Romannumerals#1%
  {\uppercase\expandafter{\romannumeral#1}}

%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>0
       \advance\!!counta by -1
       \!!countb=\!!counta
       \divide\!!counta by 26
       \!!countc=\!!counta
       \multiply\!!countc by 26
       \advance\!!countb by -\!!countc
       \doconvertcharacters#1{\!!counta}%
       \advance\!!countb by 1
       #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   {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. 

\let\normaltime  = \time
\let\normalyear  = \year
\let\normalmonth = \month
\let\normalday   = \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=\ifcase#2\relax 
     0\or  0\or 31\or 59\or 90\or120\or151\or
   181\or212\or243\or273\or304\or334\or365\fi
   \advance\!!counta by #1\relax
   \ifnum\!!counta<31
     \DoMod#3by4to\!!countb
     \ifnum\!!countb=0
       \!!doneafalse
       \DoMod#3by100to\!!countb
       \ifnum\!!countb=0 \else \!!doneatrue \fi
       \DoMod#3by400to\!!countb
       \ifnum\!!countb=0 \!!doneatrue \fi
     \else
       \!!doneafalse
     \fi
   \else
     \!!doneafalse
   \fi
   \!!countb=#3\relax
   \multiply\!!countb by 365
   \advance\!!counta by \!!countb
   \DoDiv#3by4to\!!countb
   \advance\!!counta by \!!countb
   \DoDiv#3by400to\!!countb
   \advance\!!counta by \!!countb
   \DoDiv#3by100to\!!countb
   \advance\!!counta by -\!!countb
   \if!!donea
     \advance\!!counta by -1
   \fi
   \advance\!!counta by -1
   \DoMod\!!counta by7to\!!counta
   \advance\!!counta by 1
   \@EA\egroup\@EA\normalweekday\the\!!counta\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
   \ifnum\!!countb=0
     \DoMod\!!counta by100to\!!countb
     \ifnum\!!countb=0 \else \!!doneatrue \fi
     \DoMod\!!counta by400to\!!countb
     \ifnum\!!countb=0 \!!doneatrue \fi
   \fi
   \if!!donea
     \egroup\def\next{#2}% 
   \else
     \egroup\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 \typebyffer
%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\complexcurrentdate[#1]%
  {\bgroup
   \let\labellanguage=\currentlanguage
   \def\betweendates{\let\betweendates\space}%
   \processallactionsinset
     [#1]
     [        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,
               d=>\the\normalday,
               m=>\the\normalmonth,
               j=>\the\normalyear,
               y=>\the\normalyear,
               w=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear,
      \v!kenmerk=>{\complexcurrentdate[jj,mm,dd]},
          \v!dag=>\betweendates\the\normalday,
        \v!maand=>\betweendates\month\normalmonth,
        \v!MAAND=>\betweendates\MONTH\normalmonth,
         \v!jaar=>\betweendates\the\normalyear,
      \v!weekdag=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear,
      \v!WEEKDAG=>\betweendates 
                  \bgroup
                  \let\labeltext=\LABELTEXT
                  \dayoftheweek\normalday\normalmonth\normalyear
                  \egroup,
      \s!unknown=>\commalistelement
                  \def\betweendates{\let\betweendates\space}]%
   \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 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}%
   \dostepwiserecurse{1}{\numberofdays}{1}
     {\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

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

\def\defineconversion%
  {\dodoubleargument\dodefineconversion}

\def\convertnumber#1%
  {\getvalue{\??cv#1}}

\def\doifconversiondefinedelse#1%
  {\doifdefinedelse{\??cv#1}}

%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 [\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] 
  [{\symbool[1]},{\symbool[2]},{\symbool[3]},{\symbool[4]},
   {\symbool[5]},{\symbool[6]},{\symbool[7]},{\symbool[8]}]

\defineconversion 
  [set 1]
  [$\star$,$\star\star$,$\star\star\star$,
   $\ddagger$,$\ddagger\ddagger$,$\ddagger\ddagger\ddagger$,
   $\ast$,$\ast\ast$,$\ast\ast\ast$]

\defineconversion 
  [set 2] 
  [$*$,$\dag$,$\ddag$,
   $**$,$\dag\dag$,$\ddag\ddag$,
   $***$,$\dag\dag\dag,$\ddag\ddag\ddag$]

\defineconversion 
  [set 3] 
  [$\star$,$\star\star$,$\star\star\star$,
   $\ddagger$,$\ddagger\ddagger$,
   $\P$,$\P\P$,
   $\S$,$\S\S$,
   $\ast$,$\ast\ast$]

\protect

\endinput
