PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/pear/Numbers/Roman.php

https://bitbucket.org/valmy/openx
PHP | 275 lines | 124 code | 29 blank | 122 comment | 21 complexity | a51bea5f5d911116bbc060e64bd2bfd5 MD5 | raw file
  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$
  20. // {{{ Numbers_Roman
  21. /**
  22. * Provides utilities to convert roman numerals to
  23. * arabic numbers and convert arabic numbers to roman numerals.
  24. *
  25. * Supports lower case input and output and some furthers conversion
  26. * functions.
  27. *
  28. * @access public
  29. * @author David Costa <gurugeek@php.net>
  30. * @author Sterling Hughes <sterling@php.net>
  31. * @package Numbers_Roman
  32. */
  33. class Numbers_Roman
  34. {
  35. // {{{ toNumber()
  36. /**
  37. * Converts a roman numeral to a number
  38. *
  39. * @param string $roman The roman numeral to convert
  40. * lower cased numerals are converted into
  41. * uppercase
  42. * @return integer $num The number corresponding to the
  43. * given roman numeral
  44. * @access public
  45. */
  46. function toNumber($roman)
  47. {
  48. $roman = strtoupper($roman);
  49. /*
  50. * Replacing the Numerals representing an integer higher then 4000
  51. * e.g. _X represent 10 000 _L represent 50 000 etc
  52. * we first convert them into single characters
  53. */
  54. $roman = str_replace('_V', 'S', $roman);
  55. $roman = str_replace('_X', 'R', $roman);
  56. $roman = str_replace('_L', 'Q', $roman);
  57. $roman = str_replace('_C', 'P', $roman);
  58. $roman = str_replace('_D', 'O', $roman);
  59. $roman = str_replace('_M', 'N', $roman);
  60. $conv = array(
  61. array('letter' => 'I', 'number' => 1),
  62. array('letter' => 'V', 'number' => 5),
  63. array('letter' => 'X', 'number' => 10),
  64. array('letter' => 'L', 'number' => 50),
  65. array('letter' => 'C', 'number' => 100),
  66. array('letter' => 'D', 'number' => 500),
  67. array('letter' => 'M', 'number' => 1000),
  68. array('letter' => 'S', 'number' => 5000),
  69. array('letter' => 'R', 'number' => 10000),
  70. array('letter' => 'Q', 'number' => 50000),
  71. array('letter' => 'P', 'number' => 100000),
  72. array('letter' => 'O', 'number' => 500000),
  73. array('letter' => 'N', 'number' => 1000000),
  74. array('letter' => 0, 'number' => 0)
  75. );
  76. $arabic = 0;
  77. $state = 0;
  78. $sidx = 0;
  79. $len = strlen($roman) - 1;
  80. while ($len >= 0) {
  81. $i = 0;
  82. $sidx = $len;
  83. while ($conv[$i]['number'] > 0) {
  84. if (strtoupper($roman[$sidx]) == $conv[$i]['letter']) {
  85. if ($state > $conv[$i]['number']) {
  86. $arabic -= $conv[$i]['number'];
  87. } else {
  88. $arabic += $conv[$i]['number'];
  89. $state = $conv[$i]['number'];
  90. }
  91. }
  92. $i++;
  93. }
  94. $len--;
  95. }
  96. return $arabic;
  97. }
  98. // }}}
  99. // {{{ toRoman()
  100. /**
  101. * A backwards compatibility alias for toNumeral()
  102. *
  103. * @access private
  104. */
  105. function toRoman($num, $uppercase = true)
  106. {
  107. return $this->toNumeral($num, $uppercase);
  108. }
  109. // }}}
  110. // {{{ toNumeral()
  111. /**
  112. * Converts a number to its roman numeral representation
  113. *
  114. * @param integer $num An integer between 0 and 3999
  115. * inclusive that should be converted
  116. * to a roman numeral integers higher than
  117. * 3999 are supported from version 0.1.2
  118. * Note:
  119. * For an accurate result the integer shouldn't be higher
  120. * than 5 999 999. Higher integers are still converted but
  121. * they do not reflect an historically correct Roman Numeral.
  122. *
  123. * @param bool $uppercase Uppercase output: default true
  124. *
  125. * @param bool $html Enable html overscore required for
  126. * integers over 3999. default true
  127. * @return string $roman The corresponding roman numeral
  128. *
  129. * @access public
  130. */
  131. function toNumeral($num, $uppercase = true, $html = true)
  132. {
  133. $conv = array(10 => array('X', 'C', 'M'),
  134. 5 => array('V', 'L', 'D'),
  135. 1 => array('I', 'X', 'C'));
  136. $roman = '';
  137. if ($num < 0) {
  138. return '';
  139. }
  140. $num = (int) $num;
  141. $digit = (int) ($num / 1000);
  142. $num -= $digit * 1000;
  143. while ($digit > 0) {
  144. $roman .= 'M';
  145. $digit--;
  146. }
  147. for ($i = 2; $i >= 0; $i--) {
  148. $power = pow(10, $i);
  149. $digit = (int) ($num / $power);
  150. $num -= $digit * $power;
  151. if (($digit == 9) || ($digit == 4)) {
  152. $roman .= $conv[1][$i] . $conv[$digit+1][$i];
  153. } else {
  154. if ($digit >= 5) {
  155. $roman .= $conv[5][$i];
  156. $digit -= 5;
  157. }
  158. while ($digit > 0) {
  159. $roman .= $conv[1][$i];
  160. $digit--;
  161. }
  162. }
  163. }
  164. /*
  165. * Preparing the conversion of big integers over 3999.
  166. * One of the systems used by the Romans to represent 4000 and
  167. * bigger numbers was to add an overscore on the numerals.
  168. * Because of the non ansi equivalent if the html output option
  169. * is true we will return the overline in the html code if false
  170. * we will return a _ to represent the overscore to convert from
  171. * numeral to arabic we will always expect the _ as a
  172. * representation of the html overscore.
  173. */
  174. if ($html == true) {
  175. $over = '<span style="text-decoration:overline;">';
  176. $overe = '</span>';
  177. } elseif ($html == false) {
  178. $over = '_';
  179. $overe = '';
  180. }
  181. /*
  182. * Replacing the previously produced multiple MM with the
  183. * relevant numeral e.g. for 1 000 000 the roman numeral is _M
  184. * (overscore on the M) for 900 000 is _C_M (overscore on both
  185. * the C and the M) We initially set the replace to AFS which
  186. * will be later replaced with the M.
  187. *
  188. * 500 000 is _D (overscore D) in Roman Numeral
  189. * 400 000 is _C_D (overscore on both C and D) in Roman Numeral
  190. * 100 000 is _C (overscore C) in Roman Numeral
  191. * 90 000 is _X_C (overscore on both X and C) in Roman Numeral
  192. * 50 000 is _L (overscore L) in Roman Numeral
  193. * 40 000 is _X_L (overscore on both X and L) in Roman Numeral
  194. * 10 000 is _X (overscore X) in Roman Numeral
  195. * 5 000 is _V (overscore V) in Roman Numeral
  196. * 4 000 is M _V (overscore on the V only) in Roman Numeral
  197. *
  198. * For an accurate result the integer shouldn't be higher then
  199. * 5 999 999. Higher integers are still converted but they do not
  200. * reflect an historically correct Roman Numeral.
  201. */
  202. $roman = str_replace(str_repeat('M', 1000),
  203. $over.'AFS'.$overe, $roman);
  204. $roman = str_replace(str_repeat('M', 900),
  205. $over.'C'.$overe.$over.'AFS'.$overe, $roman);
  206. $roman = str_replace(str_repeat('M', 500),
  207. $over.'D'.$overe, $roman);
  208. $roman = str_replace(str_repeat('M', 400),
  209. $over.'C'.$overe.$over.'D'.$overe, $roman);
  210. $roman = str_replace(str_repeat('M', 100),
  211. $over.'C'.$overe, $roman);
  212. $roman = str_replace(str_repeat('M', 90),
  213. $over.'X'.$overe.$over.'C'.$overe, $roman);
  214. $roman = str_replace(str_repeat('M', 50),
  215. $over.'L'.$overe, $roman);
  216. $roman = str_replace(str_repeat('M', 40),
  217. $over.'X'.$overe.$over.'L'.$overe, $roman);
  218. $roman = str_replace(str_repeat('M', 10),
  219. $over.'X'.$overe, $roman);
  220. $roman = str_replace(str_repeat('M', 5),
  221. $over.'V'.$overe, $roman);
  222. $roman = str_replace(str_repeat('M', 4),
  223. 'M'.$over.'V'.$overe, $roman);
  224. /*
  225. * Replacing AFS with M used in both 1 000 000
  226. * and 900 000
  227. */
  228. $roman = str_replace('AFS', 'M', $roman);
  229. /*
  230. * Checking for lowercase output
  231. */
  232. if ($uppercase == false) {
  233. $roman = strtolower($roman);
  234. }
  235. return $roman;
  236. }
  237. // }}}
  238. }
  239. // }}}
  240. /*
  241. * Local variables:
  242. * tab-width: 4
  243. * c-basic-offset: 4
  244. * End:
  245. */
  246. ?>