Viewing file: Roman.php (11.77 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php // // +----------------------------------------------------------------------+ // | PHP Version 4 | // +----------------------------------------------------------------------+ // | Copyright (c) 1997-2004 The PHP Group | // +----------------------------------------------------------------------+ // | This source file is subject to version 2.02 of the PHP license, | // | that is bundled with this package in the file LICENSE, and is | // | available at through the world-wide-web at | // | http://www.php.net/license/2_02.txt. | // | If you did not receive a copy of the PHP license and are unable to | // | obtain it through the world-wide-web, please send a note to | // | license@php.net so we can mail you a copy immediately. | // +----------------------------------------------------------------------+ // | Authors : David Costa <gurugeek@php.net> | // | Sterling Hughes <sterling@php.net> | // +----------------------------------------------------------------------+ // $Id: Roman.php,v 1.20 2007/06/28 10:48:41 cweiske Exp $
// {{{ Numbers_Roman
/** * Provides utilities to convert roman numerals to arabic numbers * and convert arabic numbers to roman numerals. * * @author David Costa <gurugeek@php.net> * @author Sterling Hughes <sterling@php.net> * @package Numbers_Roman */
/** * Needed for error handling */ require_once 'PEAR.php';
/** * Provides utilities to convert roman numerals to * arabic numbers and convert arabic numbers to roman numerals. * * Supports lower case input and output and some further conversion * functions. * * @access public * @author David Costa <gurugeek@php.net> * @author Sterling Hughes <sterling@php.net> * @package Numbers_Roman */ class Numbers_Roman { // {{{ toNumber()
/** * Converts a roman numeral to a number * * @param string $roman The roman numeral to convert lower cased * numerals are converted into uppercase * @return integer $num The number corresponding to the * given roman numeral * @access public */ function toNumber($roman) { /* * insure that matching works */ $roman = strtoupper($roman);
/* * remove all inapropriate characters */ $roman = preg_replace('/[^_MDCLXVUI]/', '', $roman);
/* * replace u with v, in case the number being parsed * uses a variant character set */ $roman = str_replace('U', 'V', $roman);
/* * Replacing the Numerals representing an integer higher then 4000 * e.g. _X represent 10 000 _L represent 50 000 etc * we first convert them into single characters */ $roman = str_replace('_V', 'S', $roman); $roman = str_replace('_X', 'R', $roman); $roman = str_replace('_L', 'P', $roman); $roman = str_replace('_C', 'Q', $roman); $roman = str_replace('_D', 'O', $roman); $roman = str_replace('_M', 'N', $roman);
/* * now for the conversion table. * the characters must be precisely in the same order * as the number array members they represent. This way, * their position in the string precisely represents * the array key of the corresponding number. */ $conv_chars = 'IVXLCDMSRPQON0'; $conv = array( 1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000, 0 );
/* * initialize variables */ $arabic = 0; $state = 0; $sidx = 0; $pos = 0; $len = strlen($roman) - 1;
/* * the numeral string is processed from right to left. */ while ($len >= 0) { $sidx = $len;
/* * finds the array key by checking the location * of the character in the string $conv_chars */ $pos = strpos($conv_chars, $roman[$sidx]);
/* * If a character is not found in the string, * generate an error message using PEAR_Error */ if ($pos === false) { PEAR::raiseError('Numbers_Roman::toNumber error: Invalid characters in input',0, PEAR_ERROR_TRIGGER); } else {
/* * if the precedingly processed numeral is higher, * subtract the value of the current numeral. */ if ($state > $conv[$pos]) { if (!$state2){ $arabic -= $conv[$pos]; $state2 = true; } else { PEAR::raiseError('Numbers_Roman::toNumber error: Invalid numeral order in input (multiple subtraction)',0, PEAR_ERROR_TRIGGER); }
/* * else, add the value of the numeral to our number * and remember it for the if clause directly * preceding this else */ } else { $arabic += $conv[$pos]; $state = $conv[$pos]; $state2 = false; } }
/* * move one place to the left */ $len--; }
return $arabic; }
// }}} // {{{ toRoman()
/** * A backwards compatibility alias for toNumeral() * * @access private */ function toRoman($num, $uppercase = true) { return Numbers_Roman::toNumeral($num, $uppercase); }
// }}} // {{{ toNumeral()
/** * Converts a number to its roman numeral representation * * @param integer $num An integer between 0 and 3999 * inclusive that should be converted * to a roman numeral integers higher than * 3999 are supported from version 0.1.2 * Note: * For an accurate result the integer shouldn't be higher * than 5 999 999. Higher integers are still converted but * they do not reflect an historically correct Roman Numeral. * * @param bool $uppercase Uppercase output: default true * * @param bool $html Enable html overscore required for * integers over 3999. default true * @return string $roman The corresponding roman numeral * * @access public */ function toNumeral($num, $uppercase = true, $html = true) { $conv = array(10 => array('X', 'C', 'M'), 5 => array('V', 'L', 'D'), 1 => array('I', 'X', 'C')); $roman = '';
if ($num < 0) { return ''; }
$num = (int) $num;
$digit = (int) ($num / 1000); $num -= $digit * 1000; while ($digit > 0) { $roman .= 'M'; $digit--; }
for ($i = 2; $i >= 0; $i--) { $power = pow(10, $i); $digit = (int) ($num / $power); $num -= $digit * $power;
if (($digit == 9) || ($digit == 4)) { $roman .= $conv[1][$i] . $conv[$digit+1][$i]; } else { if ($digit >= 5) { $roman .= $conv[5][$i]; $digit -= 5; }
while ($digit > 0) { $roman .= $conv[1][$i]; $digit--; } } }
/* * Preparing the conversion of big integers over 3999. * One of the systems used by the Romans to represent 4000 and * bigger numbers was to add an overscore on the numerals. * Because of the non ansi equivalent if the html output option * is true we will return the overline in the html code if false * we will return a _ to represent the overscore to convert from * numeral to arabic we will always expect the _ as a * representation of the html overscore. */ if ($html == true) { $over = '<span style="text-decoration:overline;">'; $overe = '</span>'; } elseif ($html == false) { $over = '_'; $overe = ''; }
/* * Replacing the previously produced multiple MM with the * relevant numeral e.g. for 1 000 000 the roman numeral is _M * (overscore on the M) for 900 000 is _C_M (overscore on both * the C and the M) We initially set the replace to AFS which * will be later replaced with the M. * * 500 000 is _D (overscore D) in Roman Numeral * 400 000 is _C_D (overscore on both C and D) in Roman Numeral * 100 000 is _C (overscore C) in Roman Numeral * 90 000 is _X_C (overscore on both X and C) in Roman Numeral * 50 000 is _L (overscore L) in Roman Numeral * 40 000 is _X_L (overscore on both X and L) in Roman Numeral * 10 000 is _X (overscore X) in Roman Numeral * 5 000 is _V (overscore V) in Roman Numeral * 4 000 is M _V (overscore on the V only) in Roman Numeral * * For an accurate result the integer shouldn't be higher then * 5 999 999. Higher integers are still converted but they do not * reflect an historically correct Roman Numeral. */ $roman = str_replace(str_repeat('M', 1000), $over.'AFS'.$overe, $roman); $roman = str_replace(str_repeat('M', 900), $over.'C'.$overe.$over.'AFS'.$overe, $roman); $roman = str_replace(str_repeat('M', 500), $over.'D'.$overe, $roman); $roman = str_replace(str_repeat('M', 400), $over.'C'.$overe.$over.'D'.$overe, $roman); $roman = str_replace(str_repeat('M', 100), $over.'C'.$overe, $roman); $roman = str_replace(str_repeat('M', 90), $over.'X'.$overe.$over.'C'.$overe, $roman); $roman = str_replace(str_repeat('M', 50), $over.'L'.$overe, $roman); $roman = str_replace(str_repeat('M', 40), $over.'X'.$overe.$over.'L'.$overe, $roman); $roman = str_replace(str_repeat('M', 10), $over.'X'.$overe, $roman); $roman = str_replace(str_repeat('M', 5), $over.'V'.$overe, $roman); $roman = str_replace(str_repeat('M', 4), 'M'.$over.'V'.$overe, $roman);
/* * Replacing AFS with M used in both 1 000 000 * and 900 000 */ $roman = str_replace('AFS', 'M', $roman);
/* * Make HTML output more readable by combining span tags * where possible. */ if ($html == true) { $roman = str_replace($overe.$over, '', $roman); }
/* * Checking for lowercase output */ if ($uppercase == false) { $roman = strtolower($roman); }
return $roman; }
// }}}
}
// }}}
/* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */
?>
|