/* to_roman.c -- convert a value to a roman string */
/* $Id$ */
/* Carlos Duarte, 950816/980104 */
/*
* int to_roman(unsigned int rval, char *str, size_t max);
* converts `rval', into a string that represents that value in
* roman, and write results on `str', but no more than `max' chars
* (including the final \0)
*
* The above is all that is necessary to use this file: just compile it,
* with no special options, to get code for `to_roman()' function
*
* --
*
* But, to go further:
*
* . there are 3 != implementations, each can be obtained by defining ROMAN
* to a number (1 for first, 2 for second and 3 for third)
*
* . the first is the default
*
* . also, a test is provided, define -DTEST to get it
*
* . and a demo, which is obtained with -DDEMO
*
* . and finally, a PROF, that measure time performance of the three routines,
* this is obtained with -DPROF.
* Also, with -DPROF, also -DTOP_ROMAN will be recognized as 1 .. TOP_ROMAN
* values to convert
*
* --
*
* As for the algorithms: the first, use a table with all possible
* combinations of roman digits or pairs, and keep removing
* the biggest value, while it can. This is the faster.
*
* The other two, use the same algorithm, but implemented
* on a diferent way. The roman value, is parsed from lowest
* to greatest digit (as oposed above). But, while `to_roman2'
* does a base of 10, then base of 5 way of convert, `to_roman3'
* use only a base of 10 parser and convert all possible
* 10 digits directly. `to_roman2' use symmetry to convert 6-9
* into 1-5, and then convert only 1-5.
*
* As for time speed, #1 > #3 > #2.
*/
#ifndef ROMAN
#define ROMAN 1
#endif
#if ROMAN == 1
/*=======================================================================*/
#include <stddef.h>
const static struct roman_table {
int val;
char *sym;
} r_tab[] = {
{ 1000, "M" },
{ 900, "CM" },
{ 500, "D" },
{ 400, "CD" },
{ 100, "C" },
{ 90, "XC" },
{ 50, "L" },
{ 40, "XL" },
{ 10, "X" },
{ 9, "IX" },
{ 5, "V" },
{ 4, "IV" },
{ 1, "I" }
};
int
to_roman(unsigned int rval, char *str, size_t max)
{
const struct roman_table *t;
char *s, *top;
if (max < 3)
return 0;
s = str;
top = s+(max-3); /* save space for 3 chars write */
t = r_tab;
while (rval) {
while (rval >= t->val) {
if (s > top)
return (*s=0)-1;
rval -= t->val;
*s++ = t->sym[0];
if (t->sym[1])
*s++ = t->sym[1];
}
t++;
}
*s = 0;
return s-str;
}
#endif /* ROMAN == 1 */
#if ROMAN == 2
/*=======================================================================*/
#include <stddef.h>
static const char rr_dig[] = "IVXLCDM";
int
to_roman(unsigned int rval, char *str, size_t max)
{
const char *r, *top;
char *s;
int dig;
if (max < 5)
return 0;
r = rr_dig;
s = str;
top = s+(max-5); /* ensure space for 5 chars write */
while (rval) {
if (s > top)
return (*s=0)-1;
dig = rval%10;
rval /= 10;
if (dig != 0) {
int c0, c1, c2;
c1 = r[0];
c0 = 0;
c2 = r[1];
if (dig > 5) {
dig -= 5;
c0 = r[1];
c2 = r[2];
}
switch (dig) {
case 3: *s++ = c1;
case 2: *s++ = c1;
case 1: *s++ = c1; if (c0) *s++ = c0; break;
case 4: *s++ = c2; *s++ = c1; break;
case 5: *s++ = c2; break;
}
}
if (*++r == 0 || *++r == 0 || r[1] == 0) {
top += 3; /* ensure space for 2 (5-3) chars */
if (rval) do {
if (s > top)
return (*s=0)-1;
*s++ = 'M';
} while (--rval > 0);
}
}
*s = 0;
/* reverse str */
{char*t=s-1;char*tt=str;while(t>tt){int c= *t;*t-- = *tt;*tt++ =c;}}
return s-str;
}
#endif /* ROMAN == 2 */
#if ROMAN == 3
/*=======================================================================*/
#include <stddef.h>
static const char r_dig[] = "IVXLCDM";
int
to_roman(unsigned int rval, char *str, size_t max)
{
const char *r, *top;
char *s;
int dig;
if (max < 5)
return 0;
r = r_dig;
s = str;
top = s+(max-5); /* ensure space for 5 chars write */
while (rval) {
if (s > top)
return (*s=0)-1;
dig = rval%10;
rval /= 10;
switch (dig) {
default:
case 0: break;
case 3: *s++ = r[0];
case 2: *s++ = r[0];
case 1: *s++ = r[0]; break;
case 4: *s++ = r[1]; *s++ = r[0]; break;
case 5: *s++ = r[1]; break;
case 8: *s++ = r[0];
case 7: *s++ = r[0];
case 6: *s++ = r[0]; *s++ = r[1]; break;
case 9: *s++ = r[2]; *s++ = r[0]; break;
}
if (*++r == 0 || *++r == 0 || r[1] == 0) {
top += 3; /* ensure space for 2 (5-3) chars */
if (rval) do {
if (s > top)
return (*s=0)-1;
*s++ = 'M';
} while (--rval > 0);
}
}
*s = 0;
/* reverse str */
{char*t=s-1;char*tt=str;while(t>tt){int c= *t;*t-- = *tt;*tt++ =c;}}
return s-str;
}
#endif /* ROMAN == 3 */