home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / Numbers / Roman.php next >
Encoding:
PHP Script  |  2008-07-02  |  11.8 KB  |  355 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors :  David Costa <gurugeek@php.net>                            |
  17. // |            Sterling Hughes <sterling@php.net>                        |
  18. // +----------------------------------------------------------------------+
  19. // $Id: Roman.php,v 1.20 2007/06/28 10:48:41 cweiske Exp $
  20.  
  21. // {{{ Numbers_Roman
  22.  
  23. /**
  24.  * Provides utilities to convert roman numerals to arabic numbers
  25.  * and convert arabic numbers to roman numerals.
  26.  * 
  27.  * @author David Costa <gurugeek@php.net>
  28.  * @author Sterling Hughes <sterling@php.net>
  29.  * @package Numbers_Roman
  30.  */
  31.  
  32. /**
  33.  * Needed for error handling
  34.  */
  35. require_once 'PEAR.php';
  36.  
  37. /**
  38.  * Provides utilities to convert roman numerals to
  39.  * arabic numbers and convert arabic numbers to roman numerals.
  40.  *
  41.  * Supports lower case input and output and some further conversion
  42.  * functions.
  43.  *
  44.  * @access public
  45.  * @author David Costa <gurugeek@php.net>
  46.  * @author Sterling Hughes <sterling@php.net>
  47.  * @package Numbers_Roman
  48.  */
  49. class Numbers_Roman
  50. {
  51.     // {{{ toNumber()
  52.  
  53.     /**
  54.      * Converts a roman numeral to a number
  55.      *
  56.      * @param  string  $roman The roman numeral to convert lower cased 
  57.      *                        numerals are converted into uppercase 
  58.      * @return integer $num   The number corresponding to the 
  59.      *                        given roman numeral 
  60.      * @access public
  61.      */
  62.     function toNumber($roman)
  63.     {
  64.         /*
  65.          * insure that matching works
  66.          */
  67.         $roman = strtoupper($roman);
  68.  
  69.         /*
  70.          * remove all inapropriate characters
  71.          */
  72.         $roman = preg_replace('/[^_MDCLXVUI]/', '', $roman);
  73.  
  74.         /*
  75.          * replace u with v, in case the number being parsed
  76.          * uses a variant character set
  77.          */
  78.         $roman = str_replace('U', 'V', $roman);
  79.  
  80.         /*
  81.          * Replacing the Numerals representing an integer higher then 4000
  82.          * e.g. _X represent 10 000 _L  represent 50 000 etc
  83.          * we first convert them into single characters
  84.          */
  85.         $roman = str_replace('_V', 'S', $roman);
  86.         $roman = str_replace('_X', 'R', $roman);
  87.         $roman = str_replace('_L', 'P', $roman);
  88.         $roman = str_replace('_C', 'Q', $roman);
  89.         $roman = str_replace('_D', 'O', $roman);
  90.         $roman = str_replace('_M', 'N', $roman);
  91.  
  92.         /*
  93.          * now for the conversion table.
  94.          * the characters must be precisely in the same order
  95.          * as the number array members they represent. This way,
  96.          * their position in the string precisely represents
  97.          * the array key of the corresponding number.
  98.          */
  99.         $conv_chars = 'IVXLCDMSRPQON0';
  100.         $conv = array(
  101.                         1,
  102.                         5,
  103.                         10,
  104.                         50,
  105.                         100,
  106.                         500,
  107.                         1000,
  108.                         5000,
  109.                         10000,
  110.                         50000,
  111.                         100000,
  112.                         500000,
  113.                         1000000,
  114.                         0
  115.         );
  116.  
  117.         /*
  118.          * initialize variables
  119.          */
  120.         $arabic = 0;
  121.         $state = 0;
  122.         $sidx = 0;
  123.         $pos = 0;
  124.         $len = strlen($roman) - 1;
  125.  
  126.         /*
  127.          * the numeral string is processed from right to left.
  128.          */
  129.         while ($len >= 0) {
  130.             $sidx = $len;
  131.  
  132.             /*
  133.              * finds the array key by checking the location
  134.              * of the character in the string $conv_chars
  135.              */
  136.             $pos = strpos($conv_chars, $roman[$sidx]);
  137.  
  138.             /*
  139.              * If a character is not found in the string,
  140.              * generate an error message using PEAR_Error
  141.              */
  142.             if ($pos === false) {
  143.                 PEAR::raiseError('Numbers_Roman::toNumber error: Invalid characters in input',0, PEAR_ERROR_TRIGGER);
  144.             } else {
  145.  
  146.                /*
  147.                 * if the precedingly processed numeral is higher,
  148.                 * subtract the value of the current numeral.
  149.                 */
  150.                 if ($state > $conv[$pos]) {
  151.                     if (!$state2){
  152.                         $arabic -= $conv[$pos];
  153.                         $state2 = true;
  154.                     } else {
  155.                         PEAR::raiseError('Numbers_Roman::toNumber error: Invalid numeral order in input (multiple subtraction)',0, PEAR_ERROR_TRIGGER);
  156.                     }
  157.  
  158.                    /*
  159.                     * else, add the value of the numeral to our number
  160.                     * and remember it for the if clause directly
  161.                     * preceding this else
  162.                     */
  163.                 } else {
  164.                     $arabic += $conv[$pos];
  165.                     $state = $conv[$pos];
  166.                     $state2 = false;
  167.                 }
  168.             }
  169.  
  170.             /*
  171.              * move one place to the left
  172.              */
  173.             $len--;
  174.         }
  175.  
  176.         return $arabic;
  177.     }
  178.  
  179.     // }}}
  180.     // {{{ toRoman()
  181.  
  182.     /**
  183.      * A backwards compatibility alias for toNumeral()
  184.      *
  185.      * @access private
  186.      */
  187.     function toRoman($num, $uppercase = true)
  188.     {
  189.         return Numbers_Roman::toNumeral($num, $uppercase);
  190.     }
  191.  
  192.     // }}}
  193.     // {{{ toNumeral()
  194.  
  195.     /**
  196.      * Converts a number to its roman numeral representation
  197.      *
  198.      * @param  integer $num         An integer between 0 and 3999
  199.      *                              inclusive that should be converted
  200.      *                              to a roman numeral integers higher than
  201.      *                              3999 are supported from version 0.1.2
  202.      *           Note:
  203.      *           For an accurate result the integer shouldn't be higher
  204.      *           than 5 999 999. Higher integers are still converted but
  205.      *           they do not reflect an historically correct Roman Numeral.
  206.      *
  207.      * @param  bool    $uppercase   Uppercase output: default true
  208.      *
  209.      * @param  bool    $html        Enable html overscore required for
  210.      *                              integers over 3999. default true
  211.      * @return string  $roman The corresponding roman numeral
  212.      *
  213.      * @access public
  214.      */
  215.     function toNumeral($num, $uppercase = true, $html = true)
  216.     {
  217.         $conv = array(10 => array('X', 'C', 'M'),
  218.         5 => array('V', 'L', 'D'),
  219.         1 => array('I', 'X', 'C'));
  220.         $roman = '';
  221.  
  222.         if ($num < 0) {
  223.             return '';
  224.         }
  225.  
  226.         $num = (int) $num;
  227.  
  228.         $digit = (int) ($num / 1000);
  229.         $num -= $digit * 1000;
  230.         while ($digit > 0) {
  231.             $roman .= 'M';
  232.             $digit--;
  233.         }
  234.  
  235.         for ($i = 2; $i >= 0; $i--) {
  236.             $power = pow(10, $i);
  237.             $digit = (int) ($num / $power);
  238.             $num -= $digit * $power;
  239.  
  240.             if (($digit == 9) || ($digit == 4)) {
  241.                 $roman .= $conv[1][$i] . $conv[$digit+1][$i];
  242.             } else {
  243.                 if ($digit >= 5) {
  244.                     $roman .= $conv[5][$i];
  245.                     $digit -= 5;
  246.                 }
  247.  
  248.                 while ($digit > 0) {
  249.                     $roman .= $conv[1][$i];
  250.                     $digit--;
  251.                 }
  252.             }
  253.         }
  254.  
  255.         /*
  256.          * Preparing the conversion of big integers over 3999.
  257.          * One of the systems used by the Romans  to represent 4000 and
  258.          * bigger numbers was to add an overscore on the numerals.
  259.          * Because of the non ansi equivalent if the html output option
  260.          * is true we will return the overline in the html code if false
  261.          * we will return a _ to represent the overscore to convert from
  262.          * numeral to arabic we will always expect the _ as a
  263.          * representation of the html overscore.
  264.          */
  265.         if ($html == true) {
  266.             $over = '<span style="text-decoration:overline;">';
  267.             $overe = '</span>';
  268.         } elseif ($html == false) {
  269.             $over = '_';
  270.             $overe = '';
  271.         }
  272.  
  273.         /*
  274.          * Replacing the previously produced multiple MM with the
  275.          * relevant numeral e.g. for 1 000 000 the roman numeral is _M
  276.          * (overscore on the M) for 900 000 is _C_M (overscore on both
  277.          * the C and the M) We initially set the replace to AFS which
  278.          * will be later replaced with the M.
  279.          *
  280.          * 500 000 is   _D (overscore D) in Roman Numeral
  281.          * 400 000 is _C_D (overscore on both C and D) in Roman Numeral
  282.          * 100 000 is   _C (overscore C) in Roman Numeral
  283.          *  90 000 is _X_C (overscore on both X and C) in Roman Numeral
  284.          *  50 000 is   _L (overscore L) in Roman Numeral
  285.          *  40 000 is _X_L (overscore on both X and L) in Roman Numeral
  286.          *  10 000 is   _X (overscore X) in Roman Numeral
  287.          *   5 000 is   _V (overscore V) in Roman Numeral
  288.          *   4 000 is M _V (overscore on the V only) in Roman Numeral
  289.          *
  290.          * For an accurate result the integer shouldn't be higher then
  291.          * 5 999 999. Higher integers are still converted but they do not
  292.          * reflect an historically correct Roman Numeral.
  293.          */
  294.         $roman = str_replace(str_repeat('M', 1000),
  295.                              $over.'AFS'.$overe, $roman);
  296.         $roman = str_replace(str_repeat('M', 900),
  297.                              $over.'C'.$overe.$over.'AFS'.$overe, $roman);
  298.         $roman = str_replace(str_repeat('M', 500),
  299.                              $over.'D'.$overe, $roman);
  300.         $roman = str_replace(str_repeat('M', 400),
  301.                              $over.'C'.$overe.$over.'D'.$overe, $roman);
  302.         $roman = str_replace(str_repeat('M', 100),
  303.                              $over.'C'.$overe, $roman);
  304.         $roman = str_replace(str_repeat('M', 90),
  305.                              $over.'X'.$overe.$over.'C'.$overe, $roman);
  306.         $roman = str_replace(str_repeat('M', 50),
  307.                              $over.'L'.$overe, $roman);
  308.         $roman = str_replace(str_repeat('M', 40),
  309.                              $over.'X'.$overe.$over.'L'.$overe, $roman);
  310.         $roman = str_replace(str_repeat('M', 10),
  311.                              $over.'X'.$overe, $roman);
  312.         $roman = str_replace(str_repeat('M', 5),
  313.                              $over.'V'.$overe, $roman);
  314.         $roman = str_replace(str_repeat('M', 4),
  315.                              'M'.$over.'V'.$overe, $roman);
  316.  
  317.         /*
  318.          * Replacing AFS with M used in both 1 000 000
  319.          * and 900 000
  320.          */
  321.         $roman = str_replace('AFS', 'M', $roman);
  322.  
  323.         /*
  324.          * Make HTML output more readable by combining span tags
  325.          * where possible.
  326.          */
  327.         if ($html == true) {
  328.             $roman = str_replace($overe.$over, '', $roman);
  329.         }
  330.  
  331.         /*
  332.          * Checking for lowercase output
  333.          */
  334.         if ($uppercase == false) {
  335.             $roman = strtolower($roman);
  336.         }
  337.  
  338.         return $roman;
  339.     }
  340.  
  341.     // }}}
  342.  
  343. }
  344.  
  345. // }}}
  346.  
  347. /*
  348.  * Local variables:
  349.  * tab-width: 4
  350.  * c-basic-offset: 4
  351.  * End:
  352.  */
  353.  
  354. ?>
  355.