[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * BCMath Emulation Class 5 * 6 * PHP version 5 and 7 7 * 8 * @author Jim Wigginton <[email protected]> 9 * @copyright 2019 Jim Wigginton 10 * @license http://www.opensource.org/licenses/mit-license.html MIT License 11 */ 12 13 namespace bcmath_compat; 14 15 use phpseclib3\Math\BigInteger; 16 17 /** 18 * BCMath Emulation Class 19 * 20 * @author Jim Wigginton <[email protected]> 21 * @access public 22 */ 23 abstract class BCMath 24 { 25 /** 26 * Default scale parameter for all bc math functions 27 */ 28 private static $scale; 29 30 /** 31 * Set or get default scale parameter for all bc math functions 32 * 33 * Uses the PHP 7.3+ behavior 34 * 35 * @var int $scale optional 36 */ 37 private static function scale($scale = null) 38 { 39 if (isset($scale)) { 40 self::$scale = (int) $scale; 41 } 42 return self::$scale; 43 } 44 45 /** 46 * Formats numbers 47 * 48 * Places the decimal place at the appropriate place, adds trailing 0's as appropriate, etc 49 * 50 * @var string $x 51 * @var int $scale 52 * @var int $pad 53 * @var boolean $trim 54 */ 55 private static function format($x, $scale, $pad) 56 { 57 $sign = self::isNegative($x) ? '-' : ''; 58 $x = str_replace('-', '', $x); 59 60 if (strlen($x) != $pad) { 61 $x = str_pad($x, $pad, '0', STR_PAD_LEFT); 62 } 63 $temp = $pad ? substr_replace($x, '.', -$pad, 0) : $x; 64 $temp = explode('.', $temp); 65 if ($temp[0] == '') { 66 $temp[0] = '0'; 67 } 68 if (isset($temp[1])) { 69 $temp[1] = substr($temp[1], 0, $scale); 70 $temp[1] = str_pad($temp[1], $scale, '0'); 71 } elseif ($scale) { 72 $temp[1] = str_repeat('0', $scale); 73 } 74 $result = rtrim(implode('.', $temp), '.'); 75 if ($sign == '-' && preg_match('#^0\.?0*$#', $result)) { 76 $sign = ''; 77 } 78 return $sign . $result; 79 } 80 81 /** 82 * Negativity Test 83 * 84 * @var BigInteger $x 85 */ 86 private static function isNegative($x) 87 { 88 return $x->compare(new BigInteger()) < 0; 89 } 90 91 /** 92 * Add two arbitrary precision numbers 93 * 94 * @var string $x 95 * @var string $y 96 * @var int $scale 97 * @var int $pad 98 */ 99 private static function add($x, $y, $scale, $pad) 100 { 101 $z = $x->add($y); 102 103 return self::format($z, $scale, $pad); 104 } 105 106 /** 107 * Subtract one arbitrary precision number from another 108 * 109 * @var string $x 110 * @var string $y 111 * @var int $scale 112 * @var int $pad 113 */ 114 private static function sub($x, $y, $scale, $pad) 115 { 116 $z = $x->subtract($y); 117 118 return self::format($z, $scale, $pad); 119 } 120 121 /** 122 * Multiply two arbitrary precision numbers 123 * 124 * @var string $x 125 * @var string $y 126 * @var int $scale 127 * @var int $pad 128 */ 129 private static function mul($x, $y, $scale, $pad) 130 { 131 if ($x == '0' || $y == '0') { 132 $r = '0'; 133 if ($scale) { 134 $r.= '.' . str_repeat('0', $scale); 135 } 136 return $r; 137 } 138 139 $z = $x->abs()->multiply($y->abs()); 140 $result = self::format($z, $scale, 2 * $pad); 141 142 $sign = (self::isNegative($x) ^ self::isNegative($y)) && !preg_match('#^0\.?0*$#', $result) ? '-' : ''; 143 144 return $sign . $result; 145 } 146 147 /** 148 * Divide two arbitrary precision numbers 149 * 150 * @var string $x 151 * @var string $y 152 * @var int $scale 153 * @var int $pad 154 */ 155 private static function div($x, $y, $scale, $pad) 156 { 157 if ($y == '0') { 158 // < PHP 8.0 triggered a warning 159 // >= PHP 8.0 throws an exception 160 throw new \DivisionByZeroError('Division by zero'); 161 } 162 163 $temp = '1' . str_repeat('0', $scale); 164 $temp = new BigInteger($temp); 165 list($q) = $x->multiply($temp)->divide($y); 166 167 return self::format($q, $scale, $scale); 168 } 169 170 /** 171 * Get modulus of an arbitrary precision number 172 * 173 * Uses the PHP 7.2+ behavior 174 * 175 * @var string $x 176 * @var string $y 177 * @var int $scale 178 * @var int $pad 179 */ 180 private static function mod($x, $y, $scale, $pad) 181 { 182 if ($y == '0') { 183 // < PHP 8.0 triggered a warning 184 // >= PHP 8.0 throws an exception 185 throw new \DivisionByZeroError('Division by zero'); 186 } 187 188 list($q) = $x->divide($y); 189 $z = $y->multiply($q); 190 $z = $x->subtract($z); 191 192 return self::format($z, $scale, $pad); 193 } 194 195 /** 196 * Compare two arbitrary precision numbers 197 * 198 * @var string $x 199 * @var string $y 200 * @var int $scale 201 * @var int $pad 202 */ 203 private static function comp($x, $y, $scale, $pad) 204 { 205 $x = new BigInteger($x[0] . substr($x[1], 0, $scale)); 206 $y = new BigInteger($y[0] . substr($y[1], 0, $scale)); 207 208 return $x->compare($y); 209 } 210 211 /** 212 * Raise an arbitrary precision number to another 213 * 214 * Uses the PHP 7.2+ behavior 215 * 216 * @var string $x 217 * @var string $y 218 * @var int $scale 219 * @var int $pad 220 */ 221 private static function pow($x, $y, $scale, $pad) 222 { 223 if ($y == '0') { 224 $r = '1'; 225 if ($scale) { 226 $r.= '.' . str_repeat('0', $scale); 227 } 228 return $r; 229 } 230 231 $min = defined('PHP_INT_MIN') ? PHP_INT_MIN : ~PHP_INT_MAX; 232 if (bccomp($y, PHP_INT_MAX) > 0 || bccomp($y, $min) <= 0) { 233 throw new \ValueError('bcpow(): Argument #2 ($exponent) is too large'); 234 } 235 236 $sign = self::isNegative($x) ? '-' : ''; 237 $x = $x->abs(); 238 239 $r = new BigInteger(1); 240 241 for ($i = 0; $i < abs($y); $i++) { 242 $r = $r->multiply($x); 243 } 244 245 if ($y < 0) { 246 $temp = '1' . str_repeat('0', $scale + $pad * abs($y)); 247 $temp = new BigInteger($temp); 248 list($r) = $temp->divide($r); 249 $pad = $scale; 250 } else { 251 $pad*= abs($y); 252 } 253 254 return $sign . self::format($r, $scale, $pad); 255 } 256 257 /** 258 * Raise an arbitrary precision number to another, reduced by a specified modulus 259 * 260 * @var string $x 261 * @var string $e 262 * @var string $n 263 * @var int $scale 264 * @var int $pad 265 */ 266 private static function powmod($x, $e, $n, $scale, $pad) 267 { 268 if ($e[0] == '-' || $n == '0') { 269 // < PHP 8.0 returned false 270 // >= PHP 8.0 throws an exception 271 throw new \ValueError('bcpowmod(): Argument #2 ($exponent) must be greater than or equal to 0'); 272 } 273 if ($n[0] == '-') { 274 $n = substr($n, 1); 275 } 276 if ($e == '0') { 277 return $scale ? 278 '1.' . str_repeat('0', $scale) : 279 '1'; 280 } 281 282 $x = new BigInteger($x); 283 $e = new BigInteger($e); 284 $n = new BigInteger($n); 285 286 $z = $x->powMod($e, $n); 287 288 return $scale ? 289 "$z." . str_repeat('0', $scale) : 290 "$z"; 291 } 292 293 /** 294 * Get the square root of an arbitrary precision number 295 * 296 * @var string $n 297 * @var int $scale 298 * @var int $pad 299 */ 300 private static function sqrt($n, $scale, $pad) 301 { 302 // the following is based off of the following URL: 303 // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Decimal_(base_10) 304 305 if (!is_numeric($n)) { 306 return '0'; 307 } 308 $temp = explode('.', $n); 309 $decStart = ceil(strlen($temp[0]) / 2); 310 $n = implode('', $temp); 311 if (strlen($n) % 2) { 312 $n = "0$n"; 313 } 314 $parts = str_split($n, 2); 315 $parts = array_map('intval', $parts); 316 $i = 0; 317 $p = 0; // for the first step, p = 0 318 $c = $parts[$i]; 319 $result = ''; 320 while (true) { 321 // determine the greatest digit x such that x(20p+x) <= c 322 for ($x = 1; $x <= 10; $x++) { 323 if ($x * (20 * $p + $x) > $c) { 324 $x--; 325 break; 326 } 327 } 328 $result.= $x; 329 $y = $x * (20 * $p + $x); 330 $p = 10 * $p + $x; 331 $c = 100 * ($c - $y); 332 if (isset($parts[++$i])) { 333 $c+= $parts[$i]; 334 } 335 if ((!$c && $i >= $decStart) || $i - $decStart == $scale) { 336 break; 337 } 338 if ($decStart == $i) { 339 $result.= '.'; 340 } 341 } 342 343 $result = explode('.', $result); 344 if (isset($result[1])) { 345 $result[1] = str_pad($result[1], $scale, '0'); 346 } elseif ($scale) { 347 $result[1] = str_repeat('0', $scale); 348 } 349 return implode('.', $result); 350 } 351 352 /** 353 * __callStatic Magic Method 354 * 355 * @var string $name 356 * @var array $arguments 357 */ 358 public static function __callStatic($name, $arguments) 359 { 360 static $params = [ 361 'add' => 3, 362 'comp' => 3, 363 'div' => 3, 364 'mod' => 3, 365 'mul' => 3, 366 'pow' => 3, 367 'powmod' => 4, 368 'scale' => 1, 369 'sqrt' => 2, 370 'sub' => 3 371 ]; 372 if (count($arguments) < $params[$name] - 1) { 373 $min = $params[$name] - 1; 374 throw new \ArgumentCountError("bc$name() expects at least $min parameters, " . func_num_args() . " given"); 375 } 376 if (count($arguments) > $params[$name]) { 377 $str = "bc$name() expects at most {$params[$name]} parameters, " . func_num_args() . " given"; 378 throw new \ArgumentCountError($str); 379 } 380 $numbers = array_slice($arguments, 0, $params[$name] - 1); 381 382 $ints = []; 383 switch ($name) { 384 case 'pow': 385 $ints = array_slice($numbers, count($numbers) - 1); 386 $numbers = array_slice($numbers, 0, count($numbers) - 1); 387 $names = ['exponent']; 388 break; 389 case 'powmod': 390 $ints = $numbers; 391 $numbers = []; 392 $names = ['base', 'exponent', 'modulus']; 393 break; 394 case 'sqrt': 395 $names = ['num']; 396 break; 397 default: 398 $names = ['num1', 'num2']; 399 } 400 foreach ($ints as $i => &$int) { 401 if (!is_numeric($int)) { 402 $int = '0'; 403 } 404 $pos = strpos($int, '.'); 405 if ($pos !== false) { 406 $int = substr($int, 0, $pos); 407 throw new \ValueError("bc$name(): Argument #2 (\$$names[$i]) cannot have a fractional part"); 408 } 409 } 410 foreach ($numbers as $i => $arg) { 411 $num = $i + 1; 412 switch (true) { 413 case is_bool($arg): 414 case is_numeric($arg): 415 case is_string($arg): 416 case is_object($arg) && method_exists($arg, '__toString'): 417 if (!is_bool($arg) && !is_numeric("$arg")) { 418 throw new \ValueError("bc$name: bcmath function argument is not well-formed"); 419 } 420 break; 421 // PHP >= 8.1 has deprecated the passing of nulls to string parameters 422 case is_null($arg): 423 $error = "bc$name(): Passing null to parameter #$num (\$$names[$i]) of type string is deprecated"; 424 trigger_error($error, E_USER_DEPRECATED); 425 break; 426 default: 427 $type = is_object($arg) ? get_class($arg) : gettype($arg); 428 $error = "bc$name(): Argument #$num (\$$names[$i]) must be of type string, $type given"; 429 throw new \TypeError($error); 430 } 431 } 432 if (!isset(self::$scale)) { 433 $scale = ini_get('bcmath.scale'); 434 self::$scale = $scale !== false ? max(intval($scale), 0) : 0; 435 } 436 $scale = isset($arguments[$params[$name] - 1]) ? $arguments[$params[$name] - 1] : self::$scale; 437 switch (true) { 438 case is_bool($scale): 439 case is_numeric($scale): 440 case is_string($scale) && preg_match('#0-9\.#', $scale[0]): 441 break; 442 default: 443 $type = is_object($arg) ? get_class($arg) : gettype($arg); 444 $str = "bc$name(): Argument #$params[$name] (\$scale) must be of type ?int, string given"; 445 throw new \TypeError($str); 446 } 447 $scale = (int) $scale; 448 if ($scale < 0) { 449 throw new \ValueError("bc$name(): Argument #$params[$name] (\$scale) must be between 0 and 2147483647"); 450 } 451 452 $pad = 0; 453 foreach ($numbers as &$num) { 454 if (is_bool($num)) { 455 $num = $num ? '1' : '0'; 456 } elseif (!is_numeric($num)) { 457 $num = '0'; 458 } 459 $num = explode('.', $num); 460 if (isset($num[1])) { 461 $pad = max($pad, strlen($num[1])); 462 } 463 } 464 switch ($name) { 465 case 'add': 466 case 'sub': 467 case 'mul': 468 case 'div': 469 case 'mod': 470 case 'pow': 471 foreach ($numbers as &$num) { 472 if (!isset($num[1])) { 473 $num[1] = ''; 474 } 475 $num[1] = str_pad($num[1], $pad, '0'); 476 $num = new BigInteger($num[0] . $num[1]); 477 } 478 break; 479 case 'comp': 480 foreach ($numbers as &$num) { 481 if (!isset($num[1])) { 482 $num[1] = ''; 483 } 484 $num[1] = str_pad($num[1], $pad, '0'); 485 } 486 break; 487 case 'sqrt': 488 $numbers = [$arguments[0]]; 489 } 490 491 $arguments = array_merge($numbers, $ints, [$scale, $pad]); 492 return call_user_func_array('self::' . $name, $arguments); 493 } 494 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |