<?php
/*
Arabic and Roman Numerals conversions code written 2001 by Mark Wilton-Jones.
Version 1.1.1
Updated 1/11/2003 to allow Roman Numerals optimisation to be disabled.
Updated 03/04/2006 to make it validate by default.
Please see http://www.howtocreate.co.uk/jslibs/termsOfUse.html for terms and
conditions of use.
----
ArabicToRoman( ArabicNum [, dontOptimise] )
ArabicToRoman( ArabicNum [, htmlElement, htmlElement2 [, dontOptimise]] )
The ArabicNum is a normal integer between (and including) 1 and 3999999
The htmlElement is optional. For 1000000, 500000, 100000, 50000, 10000 and 5000
the Roman Numerals are
_ _ _ _ _ _
M D C L X V
The overlines are not available in ascii so I have used an underline instead.
You can vary the html used instead by putting the the html tags as the option.
Netscape 4 does not use the overline style and instead puts nothing. If you care
(you do not need to), you can use non-standard script to put the underline there
instead, using an extra style attribute:
style=&{'text-decoration:underline;'};
The default is:
<u style="text-decoration:overline;">
for the opening tag (htmlElement) and '</u>' for the closing tag (htmlElement2).
The default behaviour of the function is to optimise numbers like 3999 to MMMIM
instead of MMMCMXCIX. If you do not want that to happen, set dontOptimise to true
when calling the ArabicToRoman function.
----
RomanToArabic( RomanNum [, htmlElement, htmlElement2] )
The RomanNum is a normal Roman number.
To replace the overlines, there are two methods. One is to use htmlElement and
htmlElement2 to specify characters to be used before and after an overline
character to represent the overline. The default is htmlElement = '_' and
htmlElement2 = ''. The other method is to use alternative lettering. The following
are predefined.
_M = N
_D = O
_C = P
_L = Q
_X = R
_V = S
*/
function ArabicToRoman( $myArabicNum, $htmlelement = false, $htmlelement2 = '</u>', $dontOptimise = false ) {
if( is_bool( $htmlelement ) ) {
$dontOptimise = $htmlelement;
$htmlelement = '<u style="text-decoration:overline;">';
}
if( $myArabicNum != floor( $myArabicNum ) ) { return "not convertable, number is not an integer."; }
if( $myArabicNum > 3999999 ) { return "not convertable, number exceeds 3999999."; }
if( $myArabicNum <= 0 ) { return "not convertable, number must be greater than 0."; }
//prepare roman numerals
$ar_to_rom[1000000] = $htmlelement."M".$htmlelement2;
$ar_to_rom[500000] = $htmlelement."D".$htmlelement2;
$ar_to_rom[100000] = $htmlelement."C".$htmlelement2;
$ar_to_rom[50000] = $htmlelement."L".$htmlelement2;
$ar_to_rom[10000] = $htmlelement."X".$htmlelement2;
$ar_to_rom[5000] = $htmlelement."V".$htmlelement2;
$ar_to_rom[1000] = "M";
$ar_to_rom[500] = "D";
$ar_to_rom[100] = "C";
$ar_to_rom[50] = "L";
$ar_to_rom[10] = "X";
$ar_to_rom[5] = "V";
$ar_to_rom[1] = "I";
$myRomanNum = '';
for( $x = 1000000; $x >= 1; $x /= 10 ) {
//start at M(bar) and work down in factors of 10 to 1.
//45xxx, 49..9xx, 49..95xx, 99..95xx and 99..9xx can be done with only two
//characters. This function optimises the output. Not doing this
//would result in the use of 4+ characters instead
switch( floor( $myArabicNum / $x ) ) {
case 1 :
$myRomanNum .= $ar_to_rom[$x];
break;
case 2 :
$myRomanNum .= $ar_to_rom[$x].$ar_to_rom[$x];
break;
case 3 :
$myRomanNum .= $ar_to_rom[$x].$ar_to_rom[$x].$ar_to_rom[$x];
break;
case 4 :
if( $dontOptimise ) {
$myRomanNum .= $ar_to_rom[$x].$ar_to_rom[5 * $x];
} else {
//optimise for 45xx, 49..95xx and 99..9xx, work out the number of 9s in a row.
$num_nines = 1; $i2 = 0; $subnum = '0.';
for( $i = 1; substr( $myArabicNum, $i, 1 ) == '9'; $i++ ) { $num_nines *= 10; $i2 = $i; }
for( $i = 1; $i <= $i2; $i++ ) { $subnum .= '9'; }
if( substr( $myArabicNum, $i2+1, 1 ) == '5' ) {
//any number of nines in a row followed by a 5 (including no 9s)
$myRomanNum .= $ar_to_rom[$x / ( 2 * $num_nines )].$ar_to_rom[5 * $x];
$subnum .= '5';
} else {
//any number of nines in a row (including no 9s)
$myRomanNum .= $ar_to_rom[$x / $num_nines].$ar_to_rom[5 * $x];
}
$myArabicNum -= $subnum * $x;
}
break;
case 5 :
$myRomanNum .= $ar_to_rom[5 * $x];
break;
case 6 :
$myRomanNum .= $ar_to_rom[5 * $x].$ar_to_rom[$x];
break;
case 7 :
$myRomanNum .= $ar_to_rom[5 * $x].$ar_to_rom[$x].$ar_to_rom[$x];
break;
case 8 :
$myRomanNum .= $ar_to_rom[5 * $x].$ar_to_rom[$x].$ar_to_rom[$x].$ar_to_rom[$x];
break;
case 9 :
if( $dontOptimise ) {
$myRomanNum .= $ar_to_rom[$x].$ar_to_rom[10 * $x];
} else {
//optimise for 99..95xx and 99..9xx, work out the number of 9s in a row.
$subnum = '0.'; $num_nines = 0.1; //trust me, it works
for( $i = 0; substr( $myArabicNum, $i, 1 ) == '9'; $i++ ) { $num_nines *= 10; $i2 = $i + 1; }
for( $i = 1; $i < $i2; $i++ ) { $subnum .= '9'; }
if( substr( $myArabicNum, $i2, 1 ) == '5' ) {
//any number of nines in a row followed by a 5 (including only one 9)
$myRomanNum .= $ar_to_rom[$x / ( 2 * $num_nines )].$ar_to_rom[10 * $x];
$subnum .= '5';
} else {
//any number of nines in a row (including only one 9)
$myRomanNum .= $ar_to_rom[$x / $num_nines].$ar_to_rom[10 * $x];
}
$myArabicNum -= $subnum * $x;
}
break;
}
//take out the bit we just did and go round again with the remainder
$myArabicNum %= $x;
}
return $myRomanNum;
}
function RomanToArabic( $myRomanNum, $htmlelement = '_', $htmlelement2 = '' ) {
//convert into single characters
$htmlelement = strtoupper(addslashes($htmlelement));
$htmlelement2 = strtoupper(addslashes($htmlelement2));
$myRomanNum = strtoupper($myRomanNum);
$myRomanNum = str_replace($htmlelement."M".$htmlelement2,"N",$myRomanNum);
$myRomanNum = str_replace($htmlelement."D".$htmlelement2,"O",$myRomanNum);
$myRomanNum = str_replace($htmlelement."C".$htmlelement2,"P",$myRomanNum);
$myRomanNum = str_replace($htmlelement."L".$htmlelement2,"Q",$myRomanNum);
$myRomanNum = str_replace($htmlelement."X".$htmlelement2,"R",$myRomanNum);
$myRomanNum = str_replace($htmlelement."V".$htmlelement2,"S",$myRomanNum);
//prepare Arabic numerals
$rom_to_ar['N'] = 1000000;
$rom_to_ar['O'] = 500000;
$rom_to_ar['P'] = 100000;
$rom_to_ar['Q'] = 50000;
$rom_to_ar['R'] = 10000;
$rom_to_ar['S'] = 5000;
$rom_to_ar['M'] = 1000;
$rom_to_ar['D'] = 500;
$rom_to_ar['C'] = 100;
$rom_to_ar['L'] = 50;
$rom_to_ar['X'] = 10;
$rom_to_ar['V'] = 5;
$rom_to_ar['I'] = 1;
$myArabicNum = 0;
if( isset( $myRomanNum ) && $myRomanNum == '' ) { return 0; }
//go forever 'till you get it
for( $x = 0; $x >= 0; $x++ ) {
//if you get to the end, you have finished
if( substr($myRomanNum,$x,1) == '' ) { return $myArabicNum; }
//check for valid characters
if( $rom_to_ar[substr($myRomanNum,$x,1)] ) {
//the character is valid. check for valid format, then do we add or delete?
if( $rom_to_ar[substr($myRomanNum,$x,1)] < $rom_to_ar[substr($myRomanNum,$x+2,1)] && $rom_to_ar[substr($myRomanNum,$x+1,1)] < $rom_to_ar[substr($myRomanNum,$x+2,1)] ) {
//for example IIX is invalid. so is VIX. so is IVX. they are ambiguous
//IXI will be allowed as it is not ambiguous. Try to show a better way of writing it.
return "not convertable, format invalid. Only one lower value character may preceed a higher value character.";
}
if( $rom_to_ar[substr($myRomanNum,$x,1)] >= $rom_to_ar[substr($myRomanNum,$x+1,1)] ) {
//add
$myArabicNum += $rom_to_ar[substr($myRomanNum,$x,1)];
} else {
//delete
$myArabicNum -= $rom_to_ar[substr($myRomanNum,$x,1)];
}
} else {
return "not convertable, invalid character '".htmlentities(substr($myRomanNum,$x,1))."' used.";
}
}
}
?>