PageRenderTime 59ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/standard/tags/release-1.5.1/library/Zend/Measure/Number.php

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