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

/library/Zend/Measure/Number.php

https://bitbucket.org/dbaltas/zend-framework-1.x-on-git
PHP | 420 lines | 270 code | 53 blank | 97 comment | 27 complexity | ad6efd65a812d850bc4b3e7bd62c533c MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, MIT
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Measure
  17. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: Number.php 24593 2012-01-05 20:35:02Z matthew $
  20. */
  21. /**
  22. * Implement needed classes
  23. */
  24. require_once 'Zend/Measure/Abstract.php';
  25. require_once 'Zend/Locale.php';
  26. /**
  27. * Class for handling number conversions
  28. *
  29. * This class can only handle numbers without precision
  30. *
  31. * @category Zend
  32. * @package Zend_Measure
  33. * @subpackage Zend_Measure_Number
  34. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  35. * @license http://framework.zend.com/license/new-bsd New BSD License
  36. */
  37. class Zend_Measure_Number extends Zend_Measure_Abstract
  38. {
  39. const STANDARD = 'DECIMAL';
  40. const BINARY = 'BINARY';
  41. const TERNARY = 'TERNARY';
  42. const QUATERNARY = 'QUATERNARY';
  43. const QUINARY = 'QUINARY';
  44. const SENARY = 'SENARY';
  45. const SEPTENARY = 'SEPTENARY';
  46. const OCTAL = 'OCTAL';
  47. const NONARY = 'NONARY';
  48. const DECIMAL = 'DECIMAL';
  49. const DUODECIMAL = 'DUODECIMAL';
  50. const HEXADECIMAL = 'HEXADECIMAL';
  51. const ROMAN = 'ROMAN';
  52. /**
  53. * Calculations for all number units
  54. *
  55. * @var array
  56. */
  57. protected $_units = array(
  58. 'BINARY' => array(2, '⑵'),
  59. 'TERNARY' => array(3, '⑶'),
  60. 'QUATERNARY' => array(4, '⑷'),
  61. 'QUINARY' => array(5, '⑸'),
  62. 'SENARY' => array(6, '⑹'),
  63. 'SEPTENARY' => array(7, '⑺'),
  64. 'OCTAL' => array(8, '⑻'),
  65. 'NONARY' => array(9, '⑼'),
  66. 'DECIMAL' => array(10, '⑽'),
  67. 'DUODECIMAL' => array(12, '⑿'),
  68. 'HEXADECIMAL' => array(16, '⒃'),
  69. 'ROMAN' => array(99, ''),
  70. 'STANDARD' => 'DECIMAL'
  71. );
  72. /**
  73. * Definition of all roman signs
  74. *
  75. * @var array $_roman
  76. */
  77. private static $_roman = array(
  78. 'I' => 1,
  79. 'A' => 4,
  80. 'V' => 5,
  81. 'B' => 9,
  82. 'X' => 10,
  83. 'E' => 40,
  84. 'L' => 50,
  85. 'F' => 90,
  86. 'C' => 100,
  87. 'G' => 400,
  88. 'D' => 500,
  89. 'H' => 900,
  90. 'M' => 1000,
  91. 'J' => 4000,
  92. 'P' => 5000,
  93. 'K' => 9000,
  94. 'Q' => 10000,
  95. 'N' => 40000,
  96. 'R' => 50000,
  97. 'W' => 90000,
  98. 'S' => 100000,
  99. 'Y' => 400000,
  100. 'T' => 500000,
  101. 'Z' => 900000,
  102. 'U' => 1000000
  103. );
  104. /**
  105. * Convertion table for roman signs
  106. *
  107. * @var array $_romanconvert
  108. */
  109. private static $_romanconvert = array(
  110. '/_V/' => '/P/',
  111. '/_X/' => '/Q/',
  112. '/_L/' => '/R/',
  113. '/_C/' => '/S/',
  114. '/_D/' => '/T/',
  115. '/_M/' => '/U/',
  116. '/IV/' => '/A/',
  117. '/IX/' => '/B/',
  118. '/XL/' => '/E/',
  119. '/XC/' => '/F/',
  120. '/CD/' => '/G/',
  121. '/CM/' => '/H/',
  122. '/M_V/'=> '/J/',
  123. '/MQ/' => '/K/',
  124. '/QR/' => '/N/',
  125. '/QS/' => '/W/',
  126. '/ST/' => '/Y/',
  127. '/SU/' => '/Z/'
  128. );
  129. /**
  130. * Zend_Measure_Abstract is an abstract class for the different measurement types
  131. *
  132. * @param integer $value Value
  133. * @param string $type (Optional) A Zend_Measure_Number Type
  134. * @param string|Zend_Locale $locale (Optional) A Zend_Locale
  135. * @throws Zend_Measure_Exception When language is unknown
  136. * @throws Zend_Measure_Exception When type is unknown
  137. */
  138. public function __construct($value, $type, $locale = null)
  139. {
  140. if (($type !== null) and (Zend_Locale::isLocale($type, null, false))) {
  141. $locale = $type;
  142. $type = null;
  143. }
  144. if ($locale === null) {
  145. $locale = new Zend_Locale();
  146. }
  147. if (!Zend_Locale::isLocale($locale, true, false)) {
  148. if (!Zend_Locale::isLocale($locale, true, false)) {
  149. require_once 'Zend/Measure/Exception.php';
  150. throw new Zend_Measure_Exception("Language (" . (string) $locale . ") is unknown");
  151. }
  152. $locale = new Zend_Locale($locale);
  153. }
  154. $this->_locale = (string) $locale;
  155. if ($type === null) {
  156. $type = $this->_units['STANDARD'];
  157. }
  158. if (isset($this->_units[$type]) === false) {
  159. require_once 'Zend/Measure/Exception.php';
  160. throw new Zend_Measure_Exception("Type ($type) is unknown");
  161. }
  162. $this->setValue($value, $type, $this->_locale);
  163. }
  164. /**
  165. * Set a new value
  166. *
  167. * @param integer $value Value
  168. * @param string $type (Optional) A Zend_Measure_Number Type
  169. * @param string|Zend_Locale $locale (Optional) A Zend_Locale Type
  170. * @throws Zend_Measure_Exception
  171. */
  172. public function setValue($value, $type = null, $locale = null)
  173. {
  174. if (empty($locale)) {
  175. $locale = $this->_locale;
  176. }
  177. if (empty($this->_units[$type])) {
  178. require_once 'Zend/Measure/Exception.php';
  179. throw new Zend_Measure_Exception('unknown type of number:' . $type);
  180. }
  181. switch($type) {
  182. case 'BINARY':
  183. preg_match('/[01]+/', $value, $ergebnis);
  184. $value = $ergebnis[0];
  185. break;
  186. case 'TERNARY':
  187. preg_match('/[012]+/', $value, $ergebnis);
  188. $value = $ergebnis[0];
  189. break;
  190. case 'QUATERNARY':
  191. preg_match('/[0123]+/', $value, $ergebnis);
  192. $value = $ergebnis[0];
  193. break;
  194. case 'QUINARY':
  195. preg_match('/[01234]+/', $value, $ergebnis);
  196. $value = $ergebnis[0];
  197. break;
  198. case 'SENARY':
  199. preg_match('/[012345]+/', $value, $ergebnis);
  200. $value = $ergebnis[0];
  201. break;
  202. case 'SEPTENARY':
  203. preg_match('/[0123456]+/', $value, $ergebnis);
  204. $value = $ergebnis[0];
  205. break;
  206. case 'OCTAL':
  207. preg_match('/[01234567]+/', $value, $ergebnis);
  208. $value = $ergebnis[0];
  209. break;
  210. case 'NONARY':
  211. preg_match('/[012345678]+/', $value, $ergebnis);
  212. $value = $ergebnis[0];
  213. break;
  214. case 'DUODECIMAL':
  215. preg_match('/[0123456789AB]+/', strtoupper($value), $ergebnis);
  216. $value = $ergebnis[0];
  217. break;
  218. case 'HEXADECIMAL':
  219. preg_match('/[0123456789ABCDEF]+/', strtoupper($value), $ergebnis);
  220. $value = $ergebnis[0];
  221. break;
  222. case 'ROMAN':
  223. preg_match('/[IVXLCDM_]+/', strtoupper($value), $ergebnis);
  224. $value = $ergebnis[0];
  225. break;
  226. default:
  227. try {
  228. $value = Zend_Locale_Format::getInteger($value, array('locale' => $locale));
  229. } catch (Exception $e) {
  230. require_once 'Zend/Measure/Exception.php';
  231. throw new Zend_Measure_Exception($e->getMessage(), $e->getCode(), $e);
  232. }
  233. if (call_user_func(Zend_Locale_Math::$comp, $value, 0) < 0) {
  234. $value = call_user_func(Zend_Locale_Math::$sqrt, call_user_func(Zend_Locale_Math::$pow, $value, 2));
  235. }
  236. break;
  237. }
  238. $this->_value = $value;
  239. $this->_type = $type;
  240. }
  241. /**
  242. * Convert input to decimal value string
  243. *
  244. * @param integer $input Input string
  245. * @param string $type Type from which to convert to decimal
  246. * @return string
  247. */
  248. private function _toDecimal($input, $type)
  249. {
  250. $value = '';
  251. // Convert base xx values
  252. if ($this->_units[$type][0] <= 16) {
  253. $split = str_split($input);
  254. $length = strlen($input);
  255. for ($x = 0; $x < $length; ++$x) {
  256. $split[$x] = hexdec($split[$x]);
  257. $value = call_user_func(Zend_Locale_Math::$add, $value,
  258. call_user_func(Zend_Locale_Math::$mul, $split[$x],
  259. call_user_func(Zend_Locale_Math::$pow, $this->_units[$type][0], ($length - $x - 1))));
  260. }
  261. }
  262. // Convert roman numbers
  263. if ($type === 'ROMAN') {
  264. $input = strtoupper($input);
  265. $input = preg_replace(array_keys(self::$_romanconvert), array_values(self::$_romanconvert), $input);
  266. $split = preg_split('//', strrev($input), -1, PREG_SPLIT_NO_EMPTY);
  267. for ($x =0; $x < sizeof($split); $x++) {
  268. if ($split[$x] == '/') {
  269. continue;
  270. }
  271. $num = self::$_roman[$split[$x]];
  272. if (($x > 0 and ($split[$x-1] != '/') and ($num < self::$_roman[$split[$x-1]]))) {
  273. $num -= $num;
  274. }
  275. $value += $num;
  276. }
  277. str_replace('/', '', $value);
  278. }
  279. return $value;
  280. }
  281. /**
  282. * Convert input to type value string
  283. *
  284. * @param integer $value Input string
  285. * @param string $type Type to convert to
  286. * @return string
  287. * @throws Zend_Measure_Exception When more than 200 digits are calculated
  288. */
  289. private function _fromDecimal($value, $type)
  290. {
  291. $tempvalue = $value;
  292. if ($this->_units[$type][0] <= 16) {
  293. $newvalue = '';
  294. $count = 200;
  295. $base = $this->_units[$type][0];
  296. while (call_user_func(Zend_Locale_Math::$comp, $value, 0, 25) <> 0) {
  297. $target = call_user_func(Zend_Locale_Math::$mod, $value, $base);
  298. $newvalue = strtoupper(dechex($target)) . $newvalue;
  299. $value = call_user_func(Zend_Locale_Math::$sub, $value, $target, 0);
  300. $value = call_user_func(Zend_Locale_Math::$div, $value, $base, 0);
  301. --$count;
  302. if ($count === 0) {
  303. require_once 'Zend/Measure/Exception.php';
  304. throw new Zend_Measure_Exception("Your value '$tempvalue' cannot be processed because it extends 200 digits");
  305. }
  306. }
  307. if ($newvalue === '') {
  308. $newvalue = '0';
  309. }
  310. }
  311. if ($type === 'ROMAN') {
  312. $i = 0;
  313. $newvalue = '';
  314. $romanval = array_values(array_reverse(self::$_roman));
  315. $romankey = array_keys(array_reverse(self::$_roman));
  316. $count = 200;
  317. while (call_user_func(Zend_Locale_Math::$comp, $value, 0, 25) <> 0) {
  318. while ($value >= $romanval[$i]) {
  319. $value -= $romanval[$i];
  320. $newvalue .= $romankey[$i];
  321. if ($value < 1) {
  322. break;
  323. }
  324. --$count;
  325. if ($count === 0) {
  326. require_once 'Zend/Measure/Exception.php';
  327. throw new Zend_Measure_Exception("Your value '$tempvalue' cannot be processed because it extends 200 digits");
  328. }
  329. }
  330. $i++;
  331. }
  332. $newvalue = str_replace('/', '', preg_replace(array_values(self::$_romanconvert), array_keys(self::$_romanconvert), $newvalue));
  333. }
  334. return $newvalue;
  335. }
  336. /**
  337. * Set a new type, and convert the value
  338. *
  339. * @param string $type New type to set
  340. * @throws Zend_Measure_Exception When a unknown type is given
  341. * @return void
  342. */
  343. public function setType($type)
  344. {
  345. if (empty($this->_units[$type]) === true) {
  346. require_once 'Zend/Measure/Exception.php';
  347. throw new Zend_Measure_Exception('Unknown type of number:' . $type);
  348. }
  349. $value = $this->_toDecimal($this->getValue(-1), $this->getType(-1));
  350. $value = $this->_fromDecimal($value, $type);
  351. $this->_value = $value;
  352. $this->_type = $type;
  353. }
  354. /**
  355. * Alias function for setType returning the converted unit
  356. * Default is 0 as this class only handles numbers without precision
  357. *
  358. * @param string $type Type to convert to
  359. * @param integer $round (Optional) Precision to add, will always be 0
  360. * @return string
  361. */
  362. public function convertTo($type, $round = 0, $locale = null)
  363. {
  364. $this->setType($type);
  365. return $this->toString($round, $locale);
  366. }
  367. }