BCMath
9 months ago
GMP
9 months ago
PHP
9 months ago
.htaccess
9 months ago
BCMath.php
9 months ago
Engine.php
9 months ago
GMP.php
9 months ago
OpenSSL.php
9 months ago
PHP.php
9 months ago
PHP32.php
9 months ago
PHP64.php
9 months ago
index.html
9 months ago
web.config
9 months ago
PHP.php
1255 lines
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * Pure-PHP BigInteger Engine |
| 5 | * |
| 6 | * PHP version 5 and 7 |
| 7 | * |
| 8 | * @author Jim Wigginton <terrafrost@php.net> |
| 9 | * @copyright 2017 Jim Wigginton |
| 10 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
| 11 | * @link http://pear.php.net/package/Math_BigInteger |
| 12 | */ |
| 13 | |
| 14 | declare(strict_types=1); |
| 15 | |
| 16 | namespace phpseclib3\Math\BigInteger\Engines; |
| 17 | |
| 18 | use phpseclib3\Common\Functions\Strings; |
| 19 | use phpseclib3\Exception\BadConfigurationException; |
| 20 | use phpseclib3\Exception\RuntimeException; |
| 21 | |
| 22 | /** |
| 23 | * Pure-PHP Engine. |
| 24 | * |
| 25 | * @author Jim Wigginton <terrafrost@php.net> |
| 26 | */ |
| 27 | abstract class PHP extends Engine |
| 28 | { |
| 29 | /**#@+ |
| 30 | * Array constants |
| 31 | * |
| 32 | * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and |
| 33 | * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. |
| 34 | * |
| 35 | */ |
| 36 | /** |
| 37 | * $result[self::VALUE] contains the value. |
| 38 | */ |
| 39 | public const VALUE = 0; |
| 40 | /** |
| 41 | * $result[self::SIGN] contains the sign. |
| 42 | */ |
| 43 | public const SIGN = 1; |
| 44 | /**#@-*/ |
| 45 | |
| 46 | /** |
| 47 | * Karatsuba Cutoff |
| 48 | * |
| 49 | * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? |
| 50 | */ |
| 51 | public const KARATSUBA_CUTOFF = 25; |
| 52 | |
| 53 | /** |
| 54 | * Can Bitwise operations be done fast? |
| 55 | * |
| 56 | * @see parent::bitwise_leftRotate() |
| 57 | * @see parent::bitwise_rightRotate() |
| 58 | */ |
| 59 | public const FAST_BITWISE = true; |
| 60 | |
| 61 | /** |
| 62 | * Engine Directory |
| 63 | * |
| 64 | * @see parent::setModExpEngine |
| 65 | */ |
| 66 | public const ENGINE_DIR = 'PHP'; |
| 67 | |
| 68 | /** |
| 69 | * Default constructor |
| 70 | * |
| 71 | * @param mixed $x integer Base-10 number or base-$base number if $base set. |
| 72 | * @return PHP |
| 73 | * @see parent::__construct() |
| 74 | */ |
| 75 | public function __construct($x = 0, int $base = 10) |
| 76 | { |
| 77 | if (!isset(static::$isValidEngine[static::class])) { |
| 78 | static::$isValidEngine[static::class] = static::isValidEngine(); |
| 79 | } |
| 80 | if (!static::$isValidEngine[static::class]) { |
| 81 | throw new BadConfigurationException(static::class . ' is not setup correctly on this system'); |
| 82 | } |
| 83 | |
| 84 | $this->value = []; |
| 85 | parent::__construct($x, $base); |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Initialize a PHP BigInteger Engine instance |
| 90 | * |
| 91 | * @see parent::__construct() |
| 92 | */ |
| 93 | protected function initialize(int $base): void |
| 94 | { |
| 95 | switch (abs($base)) { |
| 96 | case 16: |
| 97 | $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value; |
| 98 | $temp = new static(Strings::hex2bin($x), 256); |
| 99 | $this->value = $temp->value; |
| 100 | break; |
| 101 | case 10: |
| 102 | $temp = new static(); |
| 103 | |
| 104 | $multiplier = new static(); |
| 105 | $multiplier->value = [static::MAX10]; |
| 106 | |
| 107 | $x = $this->value; |
| 108 | |
| 109 | if ($x[0] == '-') { |
| 110 | $this->is_negative = true; |
| 111 | $x = substr($x, 1); |
| 112 | } |
| 113 | |
| 114 | $x = str_pad( |
| 115 | $x, |
| 116 | strlen($x) + ((static::MAX10LEN - 1) * strlen($x)) % static::MAX10LEN, |
| 117 | '0', |
| 118 | STR_PAD_LEFT |
| 119 | ); |
| 120 | while (strlen($x)) { |
| 121 | $temp = $temp->multiply($multiplier); |
| 122 | $temp = $temp->add(new static($this->int2bytes((int) substr($x, 0, static::MAX10LEN)), 256)); |
| 123 | $x = substr($x, static::MAX10LEN); |
| 124 | } |
| 125 | |
| 126 | $this->value = $temp->value; |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | /** |
| 131 | * Pads strings so that unpack may be used on them |
| 132 | */ |
| 133 | protected function pad(string $str): string |
| 134 | { |
| 135 | $length = strlen($str); |
| 136 | |
| 137 | $pad = 4 - (strlen($str) % 4); |
| 138 | |
| 139 | return str_pad($str, $length + $pad, "\0", STR_PAD_LEFT); |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Converts a BigInteger to a base-10 number. |
| 144 | */ |
| 145 | public function toString(): string |
| 146 | { |
| 147 | if (!count($this->value)) { |
| 148 | return '0'; |
| 149 | } |
| 150 | |
| 151 | $temp = clone $this; |
| 152 | $temp->bitmask = false; |
| 153 | $temp->is_negative = false; |
| 154 | |
| 155 | $divisor = new static(); |
| 156 | $divisor->value = [static::MAX10]; |
| 157 | $result = ''; |
| 158 | while (count($temp->value)) { |
| 159 | [$temp, $mod] = $temp->divide($divisor); |
| 160 | $result = str_pad( |
| 161 | (string) $mod->value[0] ?? '', |
| 162 | static::MAX10LEN, |
| 163 | '0', |
| 164 | STR_PAD_LEFT |
| 165 | ) . $result; |
| 166 | } |
| 167 | $result = ltrim($result, '0'); |
| 168 | if (empty($result)) { |
| 169 | $result = '0'; |
| 170 | } |
| 171 | |
| 172 | if ($this->is_negative) { |
| 173 | $result = '-' . $result; |
| 174 | } |
| 175 | |
| 176 | return $result; |
| 177 | } |
| 178 | |
| 179 | /** |
| 180 | * Converts a BigInteger to a byte string (eg. base-256). |
| 181 | */ |
| 182 | public function toBytes(bool $twos_compliment = false): string |
| 183 | { |
| 184 | if ($twos_compliment) { |
| 185 | return $this->toBytesHelper(); |
| 186 | } |
| 187 | |
| 188 | if (!count($this->value)) { |
| 189 | return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; |
| 190 | } |
| 191 | |
| 192 | $result = $this->bitwise_small_split(8); |
| 193 | $result = implode('', array_map('chr', $result)); |
| 194 | |
| 195 | return $this->precision > 0 ? |
| 196 | str_pad( |
| 197 | substr($result, -(($this->precision + 7) >> 3)), |
| 198 | ($this->precision + 7) >> 3, |
| 199 | chr(0), |
| 200 | STR_PAD_LEFT |
| 201 | ) : |
| 202 | $result; |
| 203 | } |
| 204 | |
| 205 | /** |
| 206 | * Performs addition. |
| 207 | */ |
| 208 | protected static function addHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): array |
| 209 | { |
| 210 | $x_size = count($x_value); |
| 211 | $y_size = count($y_value); |
| 212 | |
| 213 | if ($x_size == 0) { |
| 214 | return [ |
| 215 | self::VALUE => $y_value, |
| 216 | self::SIGN => $y_negative, |
| 217 | ]; |
| 218 | } elseif ($y_size == 0) { |
| 219 | return [ |
| 220 | self::VALUE => $x_value, |
| 221 | self::SIGN => $x_negative, |
| 222 | ]; |
| 223 | } |
| 224 | |
| 225 | // subtract, if appropriate |
| 226 | if ($x_negative != $y_negative) { |
| 227 | if ($x_value == $y_value) { |
| 228 | return [ |
| 229 | self::VALUE => [], |
| 230 | self::SIGN => false, |
| 231 | ]; |
| 232 | } |
| 233 | |
| 234 | $temp = self::subtractHelper($x_value, false, $y_value, false); |
| 235 | $temp[self::SIGN] = self::compareHelper($x_value, false, $y_value, false) > 0 ? |
| 236 | $x_negative : $y_negative; |
| 237 | |
| 238 | return $temp; |
| 239 | } |
| 240 | |
| 241 | if ($x_size < $y_size) { |
| 242 | $size = $x_size; |
| 243 | $value = $y_value; |
| 244 | } else { |
| 245 | $size = $y_size; |
| 246 | $value = $x_value; |
| 247 | } |
| 248 | |
| 249 | $value[count($value)] = 0; // just in case the carry adds an extra digit |
| 250 | |
| 251 | $carry = 0; |
| 252 | for ($i = 0, $j = 1; $j < $size; $i += 2, $j += 2) { |
| 253 | //$sum = $x_value[$j] * static::BASE_FULL + $x_value[$i] + $y_value[$j] * static::BASE_FULL + $y_value[$i] + $carry; |
| 254 | $sum = ($x_value[$j] + $y_value[$j]) * static::BASE_FULL + $x_value[$i] + $y_value[$i] + $carry; |
| 255 | $carry = $sum >= static::MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 |
| 256 | $sum = $carry ? $sum - static::MAX_DIGIT2 : $sum; |
| 257 | |
| 258 | $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31); |
| 259 | |
| 260 | $value[$i] = (int)($sum - static::BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) |
| 261 | $value[$j] = $temp; |
| 262 | } |
| 263 | |
| 264 | if ($j == $size) { // ie. if $y_size is odd |
| 265 | $sum = $x_value[$i] + $y_value[$i] + $carry; |
| 266 | $carry = $sum >= static::BASE_FULL; |
| 267 | $value[$i] = $carry ? $sum - static::BASE_FULL : $sum; |
| 268 | ++$i; // ie. let $i = $j since we've just done $value[$i] |
| 269 | } |
| 270 | |
| 271 | if ($carry) { |
| 272 | for (; $value[$i] == static::MAX_DIGIT; ++$i) { |
| 273 | $value[$i] = 0; |
| 274 | } |
| 275 | ++$value[$i]; |
| 276 | } |
| 277 | |
| 278 | return [ |
| 279 | self::VALUE => self::trim($value), |
| 280 | self::SIGN => $x_negative, |
| 281 | ]; |
| 282 | } |
| 283 | |
| 284 | /** |
| 285 | * Performs subtraction. |
| 286 | */ |
| 287 | public static function subtractHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): array |
| 288 | { |
| 289 | $x_size = count($x_value); |
| 290 | $y_size = count($y_value); |
| 291 | |
| 292 | if ($x_size == 0) { |
| 293 | return [ |
| 294 | self::VALUE => $y_value, |
| 295 | self::SIGN => !$y_negative, |
| 296 | ]; |
| 297 | } elseif ($y_size == 0) { |
| 298 | return [ |
| 299 | self::VALUE => $x_value, |
| 300 | self::SIGN => $x_negative, |
| 301 | ]; |
| 302 | } |
| 303 | |
| 304 | // add, if appropriate (ie. -$x - +$y or +$x - -$y) |
| 305 | if ($x_negative != $y_negative) { |
| 306 | $temp = self::addHelper($x_value, false, $y_value, false); |
| 307 | $temp[self::SIGN] = $x_negative; |
| 308 | |
| 309 | return $temp; |
| 310 | } |
| 311 | |
| 312 | $diff = self::compareHelper($x_value, $x_negative, $y_value, $y_negative); |
| 313 | |
| 314 | if (!$diff) { |
| 315 | return [ |
| 316 | self::VALUE => [], |
| 317 | self::SIGN => false, |
| 318 | ]; |
| 319 | } |
| 320 | |
| 321 | // switch $x and $y around, if appropriate. |
| 322 | if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) { |
| 323 | $temp = $x_value; |
| 324 | $x_value = $y_value; |
| 325 | $y_value = $temp; |
| 326 | |
| 327 | $x_negative = !$x_negative; |
| 328 | |
| 329 | $x_size = count($x_value); |
| 330 | $y_size = count($y_value); |
| 331 | } |
| 332 | |
| 333 | // at this point, $x_value should be at least as big as - if not bigger than - $y_value |
| 334 | |
| 335 | $carry = 0; |
| 336 | for ($i = 0, $j = 1; $j < $y_size; $i += 2, $j += 2) { |
| 337 | $sum = ($x_value[$j] - $y_value[$j]) * static::BASE_FULL + $x_value[$i] - $y_value[$i] - $carry; |
| 338 | |
| 339 | $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 |
| 340 | $sum = $carry ? $sum + static::MAX_DIGIT2 : $sum; |
| 341 | |
| 342 | $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31); |
| 343 | |
| 344 | $x_value[$i] = (int)($sum - static::BASE_FULL * $temp); |
| 345 | $x_value[$j] = $temp; |
| 346 | } |
| 347 | |
| 348 | if ($j == $y_size) { // ie. if $y_size is odd |
| 349 | $sum = $x_value[$i] - $y_value[$i] - $carry; |
| 350 | $carry = $sum < 0; |
| 351 | $x_value[$i] = $carry ? $sum + static::BASE_FULL : $sum; |
| 352 | ++$i; |
| 353 | } |
| 354 | |
| 355 | if ($carry) { |
| 356 | for (; !$x_value[$i]; ++$i) { |
| 357 | $x_value[$i] = static::MAX_DIGIT; |
| 358 | } |
| 359 | --$x_value[$i]; |
| 360 | } |
| 361 | |
| 362 | return [ |
| 363 | self::VALUE => self::trim($x_value), |
| 364 | self::SIGN => $x_negative, |
| 365 | ]; |
| 366 | } |
| 367 | |
| 368 | /** |
| 369 | * Performs multiplication. |
| 370 | */ |
| 371 | protected static function multiplyHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): array |
| 372 | { |
| 373 | //if ( $x_value == $y_value ) { |
| 374 | // return [ |
| 375 | // self::VALUE => self::square($x_value), |
| 376 | // self::SIGN => $x_sign != $y_value |
| 377 | // ]; |
| 378 | //} |
| 379 | |
| 380 | $x_length = count($x_value); |
| 381 | $y_length = count($y_value); |
| 382 | |
| 383 | if (!$x_length || !$y_length) { // a 0 is being multiplied |
| 384 | return [ |
| 385 | self::VALUE => [], |
| 386 | self::SIGN => false, |
| 387 | ]; |
| 388 | } |
| 389 | |
| 390 | return [ |
| 391 | self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ? |
| 392 | self::trim(self::regularMultiply($x_value, $y_value)) : |
| 393 | self::trim(self::karatsuba($x_value, $y_value)), |
| 394 | self::SIGN => $x_negative != $y_negative, |
| 395 | ]; |
| 396 | } |
| 397 | |
| 398 | /** |
| 399 | * Performs Karatsuba multiplication on two BigIntegers |
| 400 | * |
| 401 | * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and |
| 402 | * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}. |
| 403 | */ |
| 404 | private static function karatsuba(array $x_value, array $y_value): array |
| 405 | { |
| 406 | $m = min(count($x_value) >> 1, count($y_value) >> 1); |
| 407 | |
| 408 | if ($m < self::KARATSUBA_CUTOFF) { |
| 409 | return self::regularMultiply($x_value, $y_value); |
| 410 | } |
| 411 | |
| 412 | $x1 = array_slice($x_value, $m); |
| 413 | $x0 = array_slice($x_value, 0, $m); |
| 414 | $y1 = array_slice($y_value, $m); |
| 415 | $y0 = array_slice($y_value, 0, $m); |
| 416 | |
| 417 | $z2 = self::karatsuba($x1, $y1); |
| 418 | $z0 = self::karatsuba($x0, $y0); |
| 419 | |
| 420 | $z1 = self::addHelper($x1, false, $x0, false); |
| 421 | $temp = self::addHelper($y1, false, $y0, false); |
| 422 | $z1 = self::karatsuba($z1[self::VALUE], $temp[self::VALUE]); |
| 423 | $temp = self::addHelper($z2, false, $z0, false); |
| 424 | $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false); |
| 425 | |
| 426 | $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); |
| 427 | $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); |
| 428 | |
| 429 | $xy = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]); |
| 430 | $xy = self::addHelper($xy[self::VALUE], $xy[self::SIGN], $z0, false); |
| 431 | |
| 432 | return $xy[self::VALUE]; |
| 433 | } |
| 434 | |
| 435 | /** |
| 436 | * Performs long multiplication on two BigIntegers |
| 437 | * |
| 438 | * Modeled after 'multiply' in MutableBigInteger.java. |
| 439 | */ |
| 440 | protected static function regularMultiply(array $x_value, array $y_value): array |
| 441 | { |
| 442 | $x_length = count($x_value); |
| 443 | $y_length = count($y_value); |
| 444 | |
| 445 | if (!$x_length || !$y_length) { // a 0 is being multiplied |
| 446 | return []; |
| 447 | } |
| 448 | |
| 449 | $product_value = self::array_repeat(0, $x_length + $y_length); |
| 450 | |
| 451 | // the following for loop could be removed if the for loop following it |
| 452 | // (the one with nested for loops) initially set $i to 0, but |
| 453 | // doing so would also make the result in one set of unnecessary adds, |
| 454 | // since on the outermost loops first pass, $product->value[$k] is going |
| 455 | // to always be 0 |
| 456 | |
| 457 | $carry = 0; |
| 458 | for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0 |
| 459 | $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 |
| 460 | $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
| 461 | $product_value[$j] = (int)($temp - static::BASE_FULL * $carry); |
| 462 | } |
| 463 | |
| 464 | $product_value[$j] = $carry; |
| 465 | |
| 466 | // the above for loop is what the previous comment was talking about. the |
| 467 | // following for loop is the "one with nested for loops" |
| 468 | for ($i = 1; $i < $y_length; ++$i) { |
| 469 | $carry = 0; |
| 470 | |
| 471 | for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { |
| 472 | $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; |
| 473 | $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
| 474 | $product_value[$k] = (int)($temp - static::BASE_FULL * $carry); |
| 475 | } |
| 476 | |
| 477 | $product_value[$k] = $carry; |
| 478 | } |
| 479 | |
| 480 | return $product_value; |
| 481 | } |
| 482 | |
| 483 | /** |
| 484 | * Divides two BigIntegers. |
| 485 | * |
| 486 | * Returns an array whose first element contains the quotient and whose second element contains the |
| 487 | * "common residue". If the remainder would be positive, the "common residue" and the remainder are the |
| 488 | * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder |
| 489 | * and the divisor (basically, the "common residue" is the first positive modulo). |
| 490 | * |
| 491 | * @return array{static, static} |
| 492 | * @internal This function is based off of |
| 493 | * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}. |
| 494 | */ |
| 495 | protected function divideHelper(PHP $y): array |
| 496 | { |
| 497 | if (count($y->value) == 1) { |
| 498 | [$q, $r] = $this->divide_digit($this->value, $y->value[0]); |
| 499 | $quotient = new static(); |
| 500 | $remainder = new static(); |
| 501 | $quotient->value = $q; |
| 502 | $remainder->value = [$r]; |
| 503 | $quotient->is_negative = $this->is_negative != $y->is_negative; |
| 504 | return [$this->normalize($quotient), $this->normalize($remainder)]; |
| 505 | } |
| 506 | |
| 507 | $x = clone $this; |
| 508 | $y = clone $y; |
| 509 | |
| 510 | $x_sign = $x->is_negative; |
| 511 | $y_sign = $y->is_negative; |
| 512 | |
| 513 | $x->is_negative = $y->is_negative = false; |
| 514 | |
| 515 | $diff = $x->compare($y); |
| 516 | |
| 517 | if (!$diff) { |
| 518 | $temp = new static(); |
| 519 | $temp->value = [1]; |
| 520 | $temp->is_negative = $x_sign != $y_sign; |
| 521 | return [$this->normalize($temp), $this->normalize(static::$zero[static::class])]; |
| 522 | } |
| 523 | |
| 524 | if ($diff < 0) { |
| 525 | // if $x is negative, "add" $y. |
| 526 | if ($x_sign) { |
| 527 | $x = $y->subtract($x); |
| 528 | } |
| 529 | return [$this->normalize(static::$zero[static::class]), $this->normalize($x)]; |
| 530 | } |
| 531 | |
| 532 | // normalize $x and $y as described in HAC 14.23 / 14.24 |
| 533 | $msb = $y->value[count($y->value) - 1]; |
| 534 | for ($shift = 0; !($msb & static::MSB); ++$shift) { |
| 535 | $msb <<= 1; |
| 536 | } |
| 537 | $x->lshift($shift); |
| 538 | $y->lshift($shift); |
| 539 | $y_value = &$y->value; |
| 540 | |
| 541 | $x_max = count($x->value) - 1; |
| 542 | $y_max = count($y->value) - 1; |
| 543 | |
| 544 | $quotient = new static(); |
| 545 | $quotient_value = &$quotient->value; |
| 546 | $quotient_value = self::array_repeat(0, $x_max - $y_max + 1); |
| 547 | |
| 548 | static $temp, $lhs, $rhs; |
| 549 | if (!isset($temp)) { |
| 550 | $temp = new static(); |
| 551 | $lhs = new static(); |
| 552 | $rhs = new static(); |
| 553 | } |
| 554 | if (static::class != $temp::class) { |
| 555 | $temp = new static(); |
| 556 | $lhs = new static(); |
| 557 | $rhs = new static(); |
| 558 | } |
| 559 | $temp_value = &$temp->value; |
| 560 | $rhs_value = &$rhs->value; |
| 561 | |
| 562 | // $temp = $y << ($x_max - $y_max-1) in base 2**26 |
| 563 | $temp_value = array_merge(self::array_repeat(0, $x_max - $y_max), $y_value); |
| 564 | |
| 565 | while ($x->compare($temp) >= 0) { |
| 566 | // calculate the "common residue" |
| 567 | ++$quotient_value[$x_max - $y_max]; |
| 568 | $x = $x->subtract($temp); |
| 569 | $x_max = count($x->value) - 1; |
| 570 | } |
| 571 | |
| 572 | for ($i = $x_max; $i >= $y_max + 1; --$i) { |
| 573 | $x_value = &$x->value; |
| 574 | $x_window = [ |
| 575 | $x_value[$i] ?? 0, |
| 576 | $x_value[$i - 1] ?? 0, |
| 577 | $x_value[$i - 2] ?? 0, |
| 578 | ]; |
| 579 | $y_window = [ |
| 580 | $y_value[$y_max], |
| 581 | ($y_max > 0) ? $y_value[$y_max - 1] : 0, |
| 582 | ]; |
| 583 | |
| 584 | $q_index = $i - $y_max - 1; |
| 585 | if ($x_window[0] == $y_window[0]) { |
| 586 | $quotient_value[$q_index] = static::MAX_DIGIT; |
| 587 | } else { |
| 588 | $quotient_value[$q_index] = self::safe_divide( |
| 589 | $x_window[0] * static::BASE_FULL + $x_window[1], |
| 590 | $y_window[0] |
| 591 | ); |
| 592 | } |
| 593 | |
| 594 | $temp_value = [$y_window[1], $y_window[0]]; |
| 595 | |
| 596 | $lhs->value = [$quotient_value[$q_index]]; |
| 597 | $lhs = $lhs->multiply($temp); |
| 598 | |
| 599 | $rhs_value = [$x_window[2], $x_window[1], $x_window[0]]; |
| 600 | |
| 601 | while ($lhs->compare($rhs) > 0) { |
| 602 | --$quotient_value[$q_index]; |
| 603 | |
| 604 | $lhs->value = [$quotient_value[$q_index]]; |
| 605 | $lhs = $lhs->multiply($temp); |
| 606 | } |
| 607 | |
| 608 | $adjust = self::array_repeat(0, $q_index); |
| 609 | $temp_value = [$quotient_value[$q_index]]; |
| 610 | $temp = $temp->multiply($y); |
| 611 | $temp_value = &$temp->value; |
| 612 | if (count($temp_value)) { |
| 613 | $temp_value = array_merge($adjust, $temp_value); |
| 614 | } |
| 615 | |
| 616 | $x = $x->subtract($temp); |
| 617 | |
| 618 | if ($x->compare(static::$zero[static::class]) < 0) { |
| 619 | $temp_value = array_merge($adjust, $y_value); |
| 620 | $x = $x->add($temp); |
| 621 | |
| 622 | --$quotient_value[$q_index]; |
| 623 | } |
| 624 | |
| 625 | $x_max = count($x_value) - 1; |
| 626 | } |
| 627 | |
| 628 | // unnormalize the remainder |
| 629 | $x->rshift($shift); |
| 630 | |
| 631 | $quotient->is_negative = $x_sign != $y_sign; |
| 632 | |
| 633 | // calculate the "common residue", if appropriate |
| 634 | if ($x_sign) { |
| 635 | $y->rshift($shift); |
| 636 | $x = $y->subtract($x); |
| 637 | } |
| 638 | |
| 639 | return [$this->normalize($quotient), $this->normalize($x)]; |
| 640 | } |
| 641 | |
| 642 | /** |
| 643 | * Divides a BigInteger by a regular integer |
| 644 | * |
| 645 | * abc / x = a00 / x + b0 / x + c / x |
| 646 | */ |
| 647 | private static function divide_digit(array $dividend, int $divisor): array |
| 648 | { |
| 649 | $carry = 0; |
| 650 | $result = []; |
| 651 | |
| 652 | for ($i = count($dividend) - 1; $i >= 0; --$i) { |
| 653 | $temp = static::BASE_FULL * $carry + (int) $dividend[$i]; |
| 654 | $result[$i] = self::safe_divide($temp, $divisor); |
| 655 | $carry = (int)($temp - $divisor * $result[$i]); |
| 656 | } |
| 657 | |
| 658 | return [$result, $carry]; |
| 659 | } |
| 660 | |
| 661 | /** |
| 662 | * Single digit division |
| 663 | * |
| 664 | * Even if int64 is being used the division operator will return a float64 value |
| 665 | * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't |
| 666 | * have the precision of int64 this is a problem so, when int64 is being used, |
| 667 | * we'll guarantee that the dividend is divisible by first subtracting the remainder. |
| 668 | */ |
| 669 | private static function safe_divide(int $x, int $y): int |
| 670 | { |
| 671 | if (static::BASE === 26) { |
| 672 | return (int)($x / $y); |
| 673 | } |
| 674 | |
| 675 | // static::BASE === 31 |
| 676 | /** @var int */ |
| 677 | return ($x - ($x % $y)) / $y; |
| 678 | } |
| 679 | |
| 680 | /** |
| 681 | * Convert an array / boolean to a PHP BigInteger object |
| 682 | * |
| 683 | * @return static |
| 684 | */ |
| 685 | protected function convertToObj(array $arr): PHP |
| 686 | { |
| 687 | $result = new static(); |
| 688 | $result->value = $arr[self::VALUE]; |
| 689 | $result->is_negative = $arr[self::SIGN]; |
| 690 | |
| 691 | return $this->normalize($result); |
| 692 | } |
| 693 | |
| 694 | /** |
| 695 | * Normalize |
| 696 | * |
| 697 | * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision |
| 698 | * |
| 699 | * @return static |
| 700 | */ |
| 701 | protected function normalize(PHP $result): PHP |
| 702 | { |
| 703 | $result->precision = $this->precision; |
| 704 | $result->bitmask = $this->bitmask; |
| 705 | |
| 706 | $value = &$result->value; |
| 707 | |
| 708 | if (!count($value)) { |
| 709 | $result->is_negative = false; |
| 710 | return $result; |
| 711 | } |
| 712 | |
| 713 | $value = static::trim($value); |
| 714 | |
| 715 | if (!empty($result->bitmask->value)) { |
| 716 | $length = min(count($value), count($result->bitmask->value)); |
| 717 | $value = array_slice($value, 0, $length); |
| 718 | |
| 719 | for ($i = 0; $i < $length; ++$i) { |
| 720 | $value[$i] = $value[$i] & $result->bitmask->value[$i]; |
| 721 | } |
| 722 | |
| 723 | $value = static::trim($value); |
| 724 | } |
| 725 | |
| 726 | return $result; |
| 727 | } |
| 728 | |
| 729 | /** |
| 730 | * Compares two numbers. |
| 731 | * |
| 732 | * @see static::compare() |
| 733 | */ |
| 734 | protected static function compareHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): int |
| 735 | { |
| 736 | if ($x_negative != $y_negative) { |
| 737 | return (!$x_negative && $y_negative) ? 1 : -1; |
| 738 | } |
| 739 | |
| 740 | $result = $x_negative ? -1 : 1; |
| 741 | |
| 742 | if (count($x_value) != count($y_value)) { |
| 743 | return (count($x_value) > count($y_value)) ? $result : -$result; |
| 744 | } |
| 745 | $size = max(count($x_value), count($y_value)); |
| 746 | |
| 747 | $x_value = array_pad($x_value, $size, 0); |
| 748 | $y_value = array_pad($y_value, $size, 0); |
| 749 | |
| 750 | for ($i = count($x_value) - 1; $i >= 0; --$i) { |
| 751 | if ($x_value[$i] != $y_value[$i]) { |
| 752 | return ($x_value[$i] > $y_value[$i]) ? $result : -$result; |
| 753 | } |
| 754 | } |
| 755 | |
| 756 | return 0; |
| 757 | } |
| 758 | |
| 759 | /** |
| 760 | * Absolute value. |
| 761 | */ |
| 762 | public function abs(): PHP |
| 763 | { |
| 764 | $temp = new static(); |
| 765 | $temp->value = $this->value; |
| 766 | |
| 767 | return $temp; |
| 768 | } |
| 769 | |
| 770 | /** |
| 771 | * Trim |
| 772 | * |
| 773 | * Removes leading zeros |
| 774 | * |
| 775 | * @param list<static> $value |
| 776 | * @return list<static> |
| 777 | */ |
| 778 | protected static function trim(array $value): array |
| 779 | { |
| 780 | for ($i = count($value) - 1; $i >= 0; --$i) { |
| 781 | if ($value[$i]) { |
| 782 | break; |
| 783 | } |
| 784 | unset($value[$i]); |
| 785 | } |
| 786 | |
| 787 | return $value; |
| 788 | } |
| 789 | |
| 790 | /** |
| 791 | * Logical Right Shift |
| 792 | * |
| 793 | * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. |
| 794 | */ |
| 795 | public function bitwise_rightShift(int $shift): PHP |
| 796 | { |
| 797 | $temp = new static(); |
| 798 | |
| 799 | // could just replace lshift with this, but then all lshift() calls would need to be rewritten |
| 800 | // and I don't want to do that... |
| 801 | $temp->value = $this->value; |
| 802 | $temp->rshift($shift); |
| 803 | |
| 804 | return $this->normalize($temp); |
| 805 | } |
| 806 | |
| 807 | /** |
| 808 | * Logical Left Shift |
| 809 | * |
| 810 | * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. |
| 811 | */ |
| 812 | public function bitwise_leftShift(int $shift): PHP |
| 813 | { |
| 814 | $temp = new static(); |
| 815 | // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten |
| 816 | // and I don't want to do that... |
| 817 | $temp->value = $this->value; |
| 818 | $temp->lshift($shift); |
| 819 | |
| 820 | return $this->normalize($temp); |
| 821 | } |
| 822 | |
| 823 | /** |
| 824 | * Converts 32-bit integers to bytes. |
| 825 | */ |
| 826 | private static function int2bytes(int $x): string |
| 827 | { |
| 828 | return ltrim(pack('N', $x), chr(0)); |
| 829 | } |
| 830 | |
| 831 | /** |
| 832 | * Array Repeat |
| 833 | */ |
| 834 | protected static function array_repeat(int $input, int $multiplier): array |
| 835 | { |
| 836 | return $multiplier ? array_fill(0, $multiplier, $input) : []; |
| 837 | } |
| 838 | |
| 839 | /** |
| 840 | * Logical Left Shift |
| 841 | * |
| 842 | * Shifts BigInteger's by $shift bits. |
| 843 | */ |
| 844 | protected function lshift(int $shift): void |
| 845 | { |
| 846 | if ($shift == 0) { |
| 847 | return; |
| 848 | } |
| 849 | |
| 850 | $num_digits = (int)($shift / static::BASE); |
| 851 | $shift %= static::BASE; |
| 852 | $shift = 1 << $shift; |
| 853 | |
| 854 | $carry = 0; |
| 855 | |
| 856 | for ($i = 0; $i < count($this->value); ++$i) { |
| 857 | $temp = $this->value[$i] * $shift + $carry; |
| 858 | $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
| 859 | $this->value[$i] = (int)($temp - $carry * static::BASE_FULL); |
| 860 | } |
| 861 | |
| 862 | if ($carry) { |
| 863 | $this->value[count($this->value)] = $carry; |
| 864 | } |
| 865 | |
| 866 | while ($num_digits--) { |
| 867 | array_unshift($this->value, 0); |
| 868 | } |
| 869 | } |
| 870 | |
| 871 | /** |
| 872 | * Logical Right Shift |
| 873 | * |
| 874 | * Shifts BigInteger's by $shift bits. |
| 875 | */ |
| 876 | protected function rshift(int $shift): void |
| 877 | { |
| 878 | if ($shift == 0) { |
| 879 | return; |
| 880 | } |
| 881 | |
| 882 | $num_digits = (int)($shift / static::BASE); |
| 883 | $shift %= static::BASE; |
| 884 | $carry_shift = static::BASE - $shift; |
| 885 | $carry_mask = (1 << $shift) - 1; |
| 886 | |
| 887 | if ($num_digits) { |
| 888 | $this->value = array_slice($this->value, $num_digits); |
| 889 | } |
| 890 | |
| 891 | $carry = 0; |
| 892 | |
| 893 | for ($i = count($this->value) - 1; $i >= 0; --$i) { |
| 894 | $temp = $this->value[$i] >> $shift | $carry; |
| 895 | $carry = ($this->value[$i] & $carry_mask) << $carry_shift; |
| 896 | $this->value[$i] = $temp; |
| 897 | } |
| 898 | |
| 899 | $this->value = static::trim($this->value); |
| 900 | } |
| 901 | |
| 902 | /** |
| 903 | * Performs modular exponentiation. |
| 904 | */ |
| 905 | protected function powModInner(PHP $e, PHP $n): PHP |
| 906 | { |
| 907 | try { |
| 908 | $class = static::$modexpEngine[static::class]; |
| 909 | return $class::powModHelper($this, $e, $n, static::class); |
| 910 | } catch (\Exception $err) { |
| 911 | return PHP\DefaultEngine::powModHelper($this, $e, $n, static::class); |
| 912 | } |
| 913 | } |
| 914 | |
| 915 | /** |
| 916 | * Performs squaring |
| 917 | * |
| 918 | * @param list<static> $x |
| 919 | * @return list<static> |
| 920 | */ |
| 921 | protected static function square(array $x): array |
| 922 | { |
| 923 | return count($x) < 2 * self::KARATSUBA_CUTOFF ? |
| 924 | self::trim(self::baseSquare($x)) : |
| 925 | self::trim(self::karatsubaSquare($x)); |
| 926 | } |
| 927 | |
| 928 | /** |
| 929 | * Performs traditional squaring on two BigIntegers |
| 930 | * |
| 931 | * Squaring can be done faster than multiplying a number by itself can be. See |
| 932 | * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} / |
| 933 | * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information. |
| 934 | */ |
| 935 | protected static function baseSquare(array $value): array |
| 936 | { |
| 937 | if (empty($value)) { |
| 938 | return []; |
| 939 | } |
| 940 | $square_value = self::array_repeat(0, 2 * count($value)); |
| 941 | |
| 942 | for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) { |
| 943 | $i2 = $i << 1; |
| 944 | |
| 945 | $temp = $square_value[$i2] + $value[$i] * $value[$i]; |
| 946 | $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
| 947 | $square_value[$i2] = (int)($temp - static::BASE_FULL * $carry); |
| 948 | |
| 949 | // note how we start from $i+1 instead of 0 as we do in multiplication. |
| 950 | for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { |
| 951 | $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; |
| 952 | $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
| 953 | $square_value[$k] = (int)($temp - static::BASE_FULL * $carry); |
| 954 | } |
| 955 | |
| 956 | // the following line can yield values larger 2**15. at this point, PHP should switch |
| 957 | // over to floats. |
| 958 | $square_value[$i + $max_index + 1] = $carry; |
| 959 | } |
| 960 | |
| 961 | return $square_value; |
| 962 | } |
| 963 | |
| 964 | /** |
| 965 | * Performs Karatsuba "squaring" on two BigIntegers |
| 966 | * |
| 967 | * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and |
| 968 | * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}. |
| 969 | */ |
| 970 | protected static function karatsubaSquare(array $value): array |
| 971 | { |
| 972 | $m = count($value) >> 1; |
| 973 | |
| 974 | if ($m < self::KARATSUBA_CUTOFF) { |
| 975 | return self::baseSquare($value); |
| 976 | } |
| 977 | |
| 978 | $x1 = array_slice($value, $m); |
| 979 | $x0 = array_slice($value, 0, $m); |
| 980 | |
| 981 | $z2 = self::karatsubaSquare($x1); |
| 982 | $z0 = self::karatsubaSquare($x0); |
| 983 | |
| 984 | $z1 = self::addHelper($x1, false, $x0, false); |
| 985 | $z1 = self::karatsubaSquare($z1[self::VALUE]); |
| 986 | $temp = self::addHelper($z2, false, $z0, false); |
| 987 | $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false); |
| 988 | |
| 989 | $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); |
| 990 | $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); |
| 991 | |
| 992 | $xx = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]); |
| 993 | $xx = self::addHelper($xx[self::VALUE], $xx[self::SIGN], $z0, false); |
| 994 | |
| 995 | return $xx[self::VALUE]; |
| 996 | } |
| 997 | |
| 998 | /** |
| 999 | * Make the current number odd |
| 1000 | * |
| 1001 | * If the current number is odd it'll be unchanged. If it's even, one will be added to it. |
| 1002 | * |
| 1003 | * @see self::randomPrime() |
| 1004 | */ |
| 1005 | protected function make_odd(): void |
| 1006 | { |
| 1007 | $this->value[0] |= 1; |
| 1008 | } |
| 1009 | |
| 1010 | /** |
| 1011 | * Test the number against small primes. |
| 1012 | * |
| 1013 | * @see self::isPrime() |
| 1014 | */ |
| 1015 | protected function testSmallPrimes(): bool |
| 1016 | { |
| 1017 | if ($this->value == [1]) { |
| 1018 | return false; |
| 1019 | } |
| 1020 | if ($this->value == [2]) { |
| 1021 | return true; |
| 1022 | } |
| 1023 | if (~$this->value[0] & 1) { |
| 1024 | return false; |
| 1025 | } |
| 1026 | |
| 1027 | $value = $this->value; |
| 1028 | foreach (static::PRIMES as $prime) { |
| 1029 | [, $r] = self::divide_digit($value, $prime); |
| 1030 | if (!$r) { |
| 1031 | return count($value) == 1 && $value[0] == $prime; |
| 1032 | } |
| 1033 | } |
| 1034 | |
| 1035 | return true; |
| 1036 | } |
| 1037 | |
| 1038 | /** |
| 1039 | * Scan for 1 and right shift by that amount |
| 1040 | * |
| 1041 | * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); |
| 1042 | * |
| 1043 | * @return int |
| 1044 | * @see self::isPrime() |
| 1045 | */ |
| 1046 | public static function scan1divide(PHP $r) |
| 1047 | { |
| 1048 | $r_value = &$r->value; |
| 1049 | for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) { |
| 1050 | $temp = ~$r_value[$i] & static::MAX_DIGIT; |
| 1051 | for ($j = 1; ($temp >> $j) & 1; ++$j) { |
| 1052 | } |
| 1053 | if ($j <= static::BASE) { |
| 1054 | break; |
| 1055 | } |
| 1056 | } |
| 1057 | $s = static::BASE * $i + $j; |
| 1058 | $r->rshift($s); |
| 1059 | return $s; |
| 1060 | } |
| 1061 | |
| 1062 | /** |
| 1063 | * Performs exponentiation. |
| 1064 | */ |
| 1065 | protected function powHelper(PHP $n): PHP |
| 1066 | { |
| 1067 | if ($n->compare(static::$zero[static::class]) == 0) { |
| 1068 | return new static(1); |
| 1069 | } // n^0 = 1 |
| 1070 | |
| 1071 | $temp = clone $this; |
| 1072 | while (!$n->equals(static::$one[static::class])) { |
| 1073 | $temp = $temp->multiply($this); |
| 1074 | $n = $n->subtract(static::$one[static::class]); |
| 1075 | } |
| 1076 | |
| 1077 | return $temp; |
| 1078 | } |
| 1079 | |
| 1080 | /** |
| 1081 | * Is Odd? |
| 1082 | */ |
| 1083 | public function isOdd(): bool |
| 1084 | { |
| 1085 | return (bool)($this->value[0] & 1); |
| 1086 | } |
| 1087 | |
| 1088 | /** |
| 1089 | * Tests if a bit is set |
| 1090 | */ |
| 1091 | public function testBit($x): bool |
| 1092 | { |
| 1093 | $digit = (int) floor($x / static::BASE); |
| 1094 | $bit = $x % static::BASE; |
| 1095 | |
| 1096 | if (!isset($this->value[$digit])) { |
| 1097 | return false; |
| 1098 | } |
| 1099 | |
| 1100 | return (bool)($this->value[$digit] & (1 << $bit)); |
| 1101 | } |
| 1102 | |
| 1103 | /** |
| 1104 | * Is Negative? |
| 1105 | */ |
| 1106 | public function isNegative(): bool |
| 1107 | { |
| 1108 | return $this->is_negative; |
| 1109 | } |
| 1110 | |
| 1111 | /** |
| 1112 | * Negate |
| 1113 | * |
| 1114 | * Given $k, returns -$k |
| 1115 | * |
| 1116 | * @return static |
| 1117 | */ |
| 1118 | public function negate(): PHP |
| 1119 | { |
| 1120 | $temp = clone $this; |
| 1121 | $temp->is_negative = !$temp->is_negative; |
| 1122 | |
| 1123 | return $temp; |
| 1124 | } |
| 1125 | |
| 1126 | /** |
| 1127 | * Bitwise Split |
| 1128 | * |
| 1129 | * Splits BigInteger's into chunks of $split bits |
| 1130 | * |
| 1131 | * @return list<static> |
| 1132 | */ |
| 1133 | public function bitwise_split(int $split): array |
| 1134 | { |
| 1135 | if ($split < 1) { |
| 1136 | throw new RuntimeException('Offset must be greater than 1'); |
| 1137 | } |
| 1138 | |
| 1139 | $width = (int)($split / static::BASE); |
| 1140 | if (!$width) { |
| 1141 | $arr = $this->bitwise_small_split($split); |
| 1142 | return array_map(function ($digit) { |
| 1143 | $temp = new static(); |
| 1144 | $temp->value = $digit != 0 ? [$digit] : []; |
| 1145 | return $temp; |
| 1146 | }, $arr); |
| 1147 | } |
| 1148 | |
| 1149 | $vals = []; |
| 1150 | $val = $this->value; |
| 1151 | |
| 1152 | $i = $overflow = 0; |
| 1153 | $len = count($val); |
| 1154 | while ($i < $len) { |
| 1155 | $digit = []; |
| 1156 | if (!$overflow) { |
| 1157 | $digit = array_slice($val, $i, $width); |
| 1158 | $i += $width; |
| 1159 | $overflow = $split % static::BASE; |
| 1160 | if ($overflow) { |
| 1161 | $mask = (1 << $overflow) - 1; |
| 1162 | $temp = $val[$i] ?? 0; |
| 1163 | $digit[] = $temp & $mask; |
| 1164 | } |
| 1165 | } else { |
| 1166 | $remaining = static::BASE - $overflow; |
| 1167 | $tempsplit = $split - $remaining; |
| 1168 | $tempwidth = (int)($tempsplit / static::BASE + 1); |
| 1169 | $digit = array_slice($val, $i, $tempwidth); |
| 1170 | $i += $tempwidth; |
| 1171 | $tempoverflow = $tempsplit % static::BASE; |
| 1172 | if ($tempoverflow) { |
| 1173 | $tempmask = (1 << $tempoverflow) - 1; |
| 1174 | $temp = $val[$i] ?? 0; |
| 1175 | $digit[] = $temp & $tempmask; |
| 1176 | } |
| 1177 | $newbits = 0; |
| 1178 | for ($j = count($digit) - 1; $j >= 0; $j--) { |
| 1179 | $temp = $digit[$j] & $mask; |
| 1180 | $digit[$j] = ($digit[$j] >> $overflow) | ($newbits << $remaining); |
| 1181 | $newbits = $temp; |
| 1182 | } |
| 1183 | $overflow = $tempoverflow; |
| 1184 | $mask = $tempmask; |
| 1185 | } |
| 1186 | $temp = new static(); |
| 1187 | $temp->value = static::trim($digit); |
| 1188 | $vals[] = $temp; |
| 1189 | } |
| 1190 | |
| 1191 | return array_reverse($vals); |
| 1192 | } |
| 1193 | |
| 1194 | /** |
| 1195 | * Bitwise Split where $split < static::BASE |
| 1196 | * |
| 1197 | * @return list<int> |
| 1198 | */ |
| 1199 | private function bitwise_small_split(int $split): array |
| 1200 | { |
| 1201 | $vals = []; |
| 1202 | $val = $this->value; |
| 1203 | |
| 1204 | $mask = (1 << $split) - 1; |
| 1205 | |
| 1206 | $i = $overflow = 0; |
| 1207 | $len = count($val); |
| 1208 | $val[] = 0; |
| 1209 | $remaining = static::BASE; |
| 1210 | while ($i != $len) { |
| 1211 | $digit = $val[$i] & $mask; |
| 1212 | $val[$i] >>= $split; |
| 1213 | if (!$overflow) { |
| 1214 | $remaining -= $split; |
| 1215 | $overflow = $split <= $remaining ? 0 : $split - $remaining; |
| 1216 | |
| 1217 | if (!$remaining) { |
| 1218 | $i++; |
| 1219 | $remaining = static::BASE; |
| 1220 | $overflow = 0; |
| 1221 | } |
| 1222 | } elseif (++$i != $len) { |
| 1223 | $tempmask = (1 << $overflow) - 1; |
| 1224 | $digit |= ($val[$i] & $tempmask) << $remaining; |
| 1225 | $val[$i] >>= $overflow; |
| 1226 | $remaining = static::BASE - $overflow; |
| 1227 | $overflow = $split <= $remaining ? 0 : $split - $remaining; |
| 1228 | } |
| 1229 | |
| 1230 | $vals[] = $digit; |
| 1231 | } |
| 1232 | |
| 1233 | while ($vals[count($vals) - 1] == 0) { |
| 1234 | unset($vals[count($vals) - 1]); |
| 1235 | } |
| 1236 | |
| 1237 | return array_reverse($vals); |
| 1238 | } |
| 1239 | |
| 1240 | /** |
| 1241 | * @return bool |
| 1242 | */ |
| 1243 | protected static function testJITOnWindows() |
| 1244 | { |
| 1245 | // see https://github.com/php/php-src/issues/11917 |
| 1246 | if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && function_exists('opcache_get_status') && PHP_VERSION_ID < 80213 && !defined('PHPSECLIB_ALLOW_JIT')) { |
| 1247 | $status = opcache_get_status(); |
| 1248 | if ($status && isset($status['jit']) && $status['jit']['enabled'] && $status['jit']['on']) { |
| 1249 | return true; |
| 1250 | } |
| 1251 | } |
| 1252 | return false; |
| 1253 | } |
| 1254 | } |
| 1255 |