home *** CD-ROM | disk | FTP | other *** search
- <?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:
- */
-
- ?>
-