File indexing completed on 2024-12-22 05:36:52
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Measure 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * Implement needed classes 0024 */ 0025 // require_once 'Zend/Measure/Abstract.php'; 0026 // require_once 'Zend/Locale.php'; 0027 0028 /** 0029 * Class for handling number conversions 0030 * 0031 * This class can only handle numbers without precision 0032 * 0033 * @category Zend 0034 * @package Zend_Measure 0035 * @subpackage Zend_Measure_Number 0036 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0037 * @license http://framework.zend.com/license/new-bsd New BSD License 0038 */ 0039 class Zend_Measure_Number extends Zend_Measure_Abstract 0040 { 0041 const STANDARD = 'DECIMAL'; 0042 0043 const BINARY = 'BINARY'; 0044 const TERNARY = 'TERNARY'; 0045 const QUATERNARY = 'QUATERNARY'; 0046 const QUINARY = 'QUINARY'; 0047 const SENARY = 'SENARY'; 0048 const SEPTENARY = 'SEPTENARY'; 0049 const OCTAL = 'OCTAL'; 0050 const NONARY = 'NONARY'; 0051 const DECIMAL = 'DECIMAL'; 0052 const DUODECIMAL = 'DUODECIMAL'; 0053 const HEXADECIMAL = 'HEXADECIMAL'; 0054 const ROMAN = 'ROMAN'; 0055 0056 /** 0057 * Calculations for all number units 0058 * 0059 * @var array 0060 */ 0061 protected $_units = array( 0062 'BINARY' => array(2, '⑵'), 0063 'TERNARY' => array(3, '⑶'), 0064 'QUATERNARY' => array(4, '⑷'), 0065 'QUINARY' => array(5, '⑸'), 0066 'SENARY' => array(6, '⑹'), 0067 'SEPTENARY' => array(7, '⑺'), 0068 'OCTAL' => array(8, '⑻'), 0069 'NONARY' => array(9, '⑼'), 0070 'DECIMAL' => array(10, '⑽'), 0071 'DUODECIMAL' => array(12, '⑿'), 0072 'HEXADECIMAL' => array(16, '⒃'), 0073 'ROMAN' => array(99, ''), 0074 'STANDARD' => 'DECIMAL' 0075 ); 0076 0077 /** 0078 * Definition of all roman signs 0079 * 0080 * @var array $_roman 0081 */ 0082 private static $_roman = array( 0083 'I' => 1, 0084 'A' => 4, 0085 'V' => 5, 0086 'B' => 9, 0087 'X' => 10, 0088 'E' => 40, 0089 'L' => 50, 0090 'F' => 90, 0091 'C' => 100, 0092 'G' => 400, 0093 'D' => 500, 0094 'H' => 900, 0095 'M' => 1000, 0096 'J' => 4000, 0097 'P' => 5000, 0098 'K' => 9000, 0099 'Q' => 10000, 0100 'N' => 40000, 0101 'R' => 50000, 0102 'W' => 90000, 0103 'S' => 100000, 0104 'Y' => 400000, 0105 'T' => 500000, 0106 'Z' => 900000, 0107 'U' => 1000000 0108 ); 0109 0110 /** 0111 * Convertion table for roman signs 0112 * 0113 * @var array $_romanconvert 0114 */ 0115 private static $_romanconvert = array( 0116 '/_V/' => '/P/', 0117 '/_X/' => '/Q/', 0118 '/_L/' => '/R/', 0119 '/_C/' => '/S/', 0120 '/_D/' => '/T/', 0121 '/_M/' => '/U/', 0122 '/IV/' => '/A/', 0123 '/IX/' => '/B/', 0124 '/XL/' => '/E/', 0125 '/XC/' => '/F/', 0126 '/CD/' => '/G/', 0127 '/CM/' => '/H/', 0128 '/M_V/'=> '/J/', 0129 '/MQ/' => '/K/', 0130 '/QR/' => '/N/', 0131 '/QS/' => '/W/', 0132 '/ST/' => '/Y/', 0133 '/SU/' => '/Z/' 0134 ); 0135 0136 /** 0137 * Zend_Measure_Abstract is an abstract class for the different measurement types 0138 * 0139 * @param integer $value Value 0140 * @param string $type (Optional) A Zend_Measure_Number Type 0141 * @param string|Zend_Locale $locale (Optional) A Zend_Locale 0142 * @throws Zend_Measure_Exception When language is unknown 0143 * @throws Zend_Measure_Exception When type is unknown 0144 */ 0145 public function __construct($value, $type, $locale = null) 0146 { 0147 if (($type !== null) and (Zend_Locale::isLocale($type, null, false))) { 0148 $locale = $type; 0149 $type = null; 0150 } 0151 0152 if ($locale === null) { 0153 $locale = new Zend_Locale(); 0154 } 0155 0156 if (!Zend_Locale::isLocale($locale, true, false)) { 0157 if (!Zend_Locale::isLocale($locale, true, false)) { 0158 // require_once 'Zend/Measure/Exception.php'; 0159 throw new Zend_Measure_Exception("Language (" . (string) $locale . ") is unknown"); 0160 } 0161 0162 $locale = new Zend_Locale($locale); 0163 } 0164 0165 $this->_locale = (string) $locale; 0166 0167 if ($type === null) { 0168 $type = $this->_units['STANDARD']; 0169 } 0170 0171 if (isset($this->_units[$type]) === false) { 0172 // require_once 'Zend/Measure/Exception.php'; 0173 throw new Zend_Measure_Exception("Type ($type) is unknown"); 0174 } 0175 0176 $this->setValue($value, $type, $this->_locale); 0177 } 0178 0179 /** 0180 * Set a new value 0181 * 0182 * @param integer $value Value 0183 * @param string $type (Optional) A Zend_Measure_Number Type 0184 * @param string|Zend_Locale $locale (Optional) A Zend_Locale Type 0185 * @throws Zend_Measure_Exception 0186 */ 0187 public function setValue($value, $type = null, $locale = null) 0188 { 0189 if (empty($locale)) { 0190 $locale = $this->_locale; 0191 } 0192 0193 if (empty($this->_units[$type])) { 0194 // require_once 'Zend/Measure/Exception.php'; 0195 throw new Zend_Measure_Exception('unknown type of number:' . $type); 0196 } 0197 0198 switch($type) { 0199 case 'BINARY': 0200 preg_match('/[01]+/', $value, $ergebnis); 0201 $value = $ergebnis[0]; 0202 break; 0203 0204 case 'TERNARY': 0205 preg_match('/[012]+/', $value, $ergebnis); 0206 $value = $ergebnis[0]; 0207 break; 0208 0209 case 'QUATERNARY': 0210 preg_match('/[0123]+/', $value, $ergebnis); 0211 $value = $ergebnis[0]; 0212 break; 0213 0214 case 'QUINARY': 0215 preg_match('/[01234]+/', $value, $ergebnis); 0216 $value = $ergebnis[0]; 0217 break; 0218 0219 case 'SENARY': 0220 preg_match('/[012345]+/', $value, $ergebnis); 0221 $value = $ergebnis[0]; 0222 break; 0223 0224 case 'SEPTENARY': 0225 preg_match('/[0123456]+/', $value, $ergebnis); 0226 $value = $ergebnis[0]; 0227 break; 0228 0229 case 'OCTAL': 0230 preg_match('/[01234567]+/', $value, $ergebnis); 0231 $value = $ergebnis[0]; 0232 break; 0233 0234 case 'NONARY': 0235 preg_match('/[012345678]+/', $value, $ergebnis); 0236 $value = $ergebnis[0]; 0237 break; 0238 0239 case 'DUODECIMAL': 0240 preg_match('/[0123456789AB]+/', strtoupper($value), $ergebnis); 0241 $value = $ergebnis[0]; 0242 break; 0243 0244 case 'HEXADECIMAL': 0245 preg_match('/[0123456789ABCDEF]+/', strtoupper($value), $ergebnis); 0246 $value = $ergebnis[0]; 0247 break; 0248 0249 case 'ROMAN': 0250 preg_match('/[IVXLCDM_]+/', strtoupper($value), $ergebnis); 0251 $value = $ergebnis[0]; 0252 break; 0253 0254 default: 0255 try { 0256 $value = Zend_Locale_Format::getInteger($value, array('locale' => $locale)); 0257 } catch (Exception $e) { 0258 // require_once 'Zend/Measure/Exception.php'; 0259 throw new Zend_Measure_Exception($e->getMessage(), $e->getCode(), $e); 0260 } 0261 if (call_user_func(Zend_Locale_Math::$comp, $value, 0) < 0) { 0262 $value = call_user_func(Zend_Locale_Math::$sqrt, call_user_func(Zend_Locale_Math::$pow, $value, 2)); 0263 } 0264 break; 0265 } 0266 0267 $this->_value = $value; 0268 $this->_type = $type; 0269 } 0270 0271 /** 0272 * Convert input to decimal value string 0273 * 0274 * @param integer $input Input string 0275 * @param string $type Type from which to convert to decimal 0276 * @return string 0277 */ 0278 private function _toDecimal($input, $type) 0279 { 0280 $value = ''; 0281 // Convert base xx values 0282 if ($this->_units[$type][0] <= 16) { 0283 $split = str_split($input); 0284 $length = strlen($input); 0285 for ($x = 0; $x < $length; ++$x) { 0286 $split[$x] = hexdec($split[$x]); 0287 $value = call_user_func(Zend_Locale_Math::$add, $value, 0288 call_user_func(Zend_Locale_Math::$mul, $split[$x], 0289 call_user_func(Zend_Locale_Math::$pow, $this->_units[$type][0], ($length - $x - 1)))); 0290 } 0291 } 0292 0293 // Convert roman numbers 0294 if ($type === 'ROMAN') { 0295 $input = strtoupper($input); 0296 $input = preg_replace(array_keys(self::$_romanconvert), array_values(self::$_romanconvert), $input); 0297 0298 $split = preg_split('//', strrev($input), -1, PREG_SPLIT_NO_EMPTY); 0299 0300 for ($x =0; $x < sizeof($split); $x++) { 0301 if ($split[$x] == '/') { 0302 continue; 0303 } 0304 0305 $num = self::$_roman[$split[$x]]; 0306 if (($x > 0 and ($split[$x-1] != '/') and ($num < self::$_roman[$split[$x-1]]))) { 0307 $num -= $num; 0308 } 0309 0310 $value += $num; 0311 } 0312 0313 str_replace('/', '', $value); 0314 } 0315 0316 return $value; 0317 } 0318 0319 /** 0320 * Convert input to type value string 0321 * 0322 * @param integer $value Input string 0323 * @param string $type Type to convert to 0324 * @return string 0325 * @throws Zend_Measure_Exception When more than 200 digits are calculated 0326 */ 0327 private function _fromDecimal($value, $type) 0328 { 0329 $tempvalue = $value; 0330 if ($this->_units[$type][0] <= 16) { 0331 $newvalue = ''; 0332 $count = 200; 0333 $base = $this->_units[$type][0]; 0334 0335 while (call_user_func(Zend_Locale_Math::$comp, $value, 0, 25) <> 0) { 0336 $target = call_user_func(Zend_Locale_Math::$mod, $value, $base); 0337 0338 $newvalue = strtoupper(dechex($target)) . $newvalue; 0339 0340 $value = call_user_func(Zend_Locale_Math::$sub, $value, $target, 0); 0341 $value = call_user_func(Zend_Locale_Math::$div, $value, $base, 0); 0342 0343 --$count; 0344 if ($count === 0) { 0345 // require_once 'Zend/Measure/Exception.php'; 0346 throw new Zend_Measure_Exception("Your value '$tempvalue' cannot be processed because it extends 200 digits"); 0347 } 0348 } 0349 0350 if ($newvalue === '') { 0351 $newvalue = '0'; 0352 } 0353 } 0354 0355 if ($type === 'ROMAN') { 0356 $i = 0; 0357 $newvalue = ''; 0358 $romanval = array_values(array_reverse(self::$_roman)); 0359 $romankey = array_keys(array_reverse(self::$_roman)); 0360 $count = 200; 0361 while (call_user_func(Zend_Locale_Math::$comp, $value, 0, 25) <> 0) { 0362 while ($value >= $romanval[$i]) { 0363 $value -= $romanval[$i]; 0364 $newvalue .= $romankey[$i]; 0365 0366 if ($value < 1) { 0367 break; 0368 } 0369 0370 --$count; 0371 if ($count === 0) { 0372 // require_once 'Zend/Measure/Exception.php'; 0373 throw new Zend_Measure_Exception("Your value '$tempvalue' cannot be processed because it extends 200 digits"); 0374 } 0375 } 0376 0377 $i++; 0378 } 0379 0380 $newvalue = str_replace('/', '', preg_replace(array_values(self::$_romanconvert), array_keys(self::$_romanconvert), $newvalue)); 0381 } 0382 0383 return $newvalue; 0384 } 0385 0386 /** 0387 * Set a new type, and convert the value 0388 * 0389 * @param string $type New type to set 0390 * @throws Zend_Measure_Exception When a unknown type is given 0391 * @return void 0392 */ 0393 public function setType($type) 0394 { 0395 if (empty($this->_units[$type]) === true) { 0396 // require_once 'Zend/Measure/Exception.php'; 0397 throw new Zend_Measure_Exception('Unknown type of number:' . $type); 0398 } 0399 0400 $value = $this->_toDecimal($this->getValue(-1), $this->getType(-1)); 0401 $value = $this->_fromDecimal($value, $type); 0402 0403 $this->_value = $value; 0404 $this->_type = $type; 0405 } 0406 0407 /** 0408 * Alias function for setType returning the converted unit 0409 * Default is 0 as this class only handles numbers without precision 0410 * 0411 * @param string $type Type to convert to 0412 * @param integer $round (Optional) Precision to add, will always be 0 0413 * @return string 0414 */ 0415 public function convertTo($type, $round = 0, $locale = null) 0416 { 0417 $this->setType($type); 0418 0419 // Roman numerals do not need a formatting 0420 if ($this->getType() === self::ROMAN) { 0421 return $this->_value; 0422 } 0423 0424 return $this->toString($round, $locale); 0425 } 0426 }