[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/ -> BCMath.php (source)

   1  <?php
   2  
   3  /**
   4   * BCMath 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  namespace phpseclib3\Math\BigInteger\Engines;
  15  
  16  use phpseclib3\Common\Functions\Strings;
  17  use phpseclib3\Exception\BadConfigurationException;
  18  
  19  /**
  20   * BCMath Engine.
  21   *
  22   * @author  Jim Wigginton <terrafrost@php.net>
  23   */
  24  class BCMath extends Engine
  25  {
  26      /**
  27       * Can Bitwise operations be done fast?
  28       *
  29       * @see parent::bitwise_leftRotate()
  30       * @see parent::bitwise_rightRotate()
  31       */
  32      const FAST_BITWISE = false;
  33  
  34      /**
  35       * Engine Directory
  36       *
  37       * @see parent::setModExpEngine
  38       */
  39      const ENGINE_DIR = 'BCMath';
  40  
  41      /**
  42       * Test for engine validity
  43       *
  44       * @return bool
  45       * @see parent::__construct()
  46       */
  47      public static function isValidEngine()
  48      {
  49          return extension_loaded('bcmath');
  50      }
  51  
  52      /**
  53       * Default constructor
  54       *
  55       * @param mixed $x integer Base-10 number or base-$base number if $base set.
  56       * @param int $base
  57       * @see parent::__construct()
  58       */
  59      public function __construct($x = 0, $base = 10)
  60      {
  61          if (!isset(static::$isValidEngine[static::class])) {
  62              static::$isValidEngine[static::class] = self::isValidEngine();
  63          }
  64          if (!static::$isValidEngine[static::class]) {
  65              throw new BadConfigurationException('BCMath is not setup correctly on this system');
  66          }
  67  
  68          $this->value = '0';
  69  
  70          parent::__construct($x, $base);
  71      }
  72  
  73      /**
  74       * Initialize a BCMath BigInteger Engine instance
  75       *
  76       * @param int $base
  77       * @see parent::__construct()
  78       */
  79      protected function initialize($base)
  80      {
  81          switch (abs($base)) {
  82              case 256:
  83                  // round $len to the nearest 4
  84                  $len = (strlen($this->value) + 3) & ~3;
  85  
  86                  $x = str_pad($this->value, $len, chr(0), STR_PAD_LEFT);
  87  
  88                  $this->value = '0';
  89                  for ($i = 0; $i < $len; $i += 4) {
  90                      $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
  91                      $this->value = bcadd(
  92                          $this->value,
  93                          0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord(
  94                              $x[$i + 2]
  95                          ) << 8) | ord($x[$i + 3])),
  96                          0
  97                      );
  98                  }
  99  
 100                  if ($this->is_negative) {
 101                      $this->value = '-' . $this->value;
 102                  }
 103                  break;
 104              case 16:
 105                  $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value;
 106                  $temp = new self(Strings::hex2bin($x), 256);
 107                  $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
 108                  $this->is_negative = false;
 109                  break;
 110              case 10:
 111                  // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
 112                  // results then doing it on '-1' does (modInverse does $x[0])
 113                  $this->value = $this->value === '-' ? '0' : (string)$this->value;
 114          }
 115      }
 116  
 117      /**
 118       * Converts a BigInteger to a base-10 number.
 119       *
 120       * @return string
 121       */
 122      public function toString()
 123      {
 124          if ($this->value === '0') {
 125              return '0';
 126          }
 127  
 128          return ltrim($this->value, '0');
 129      }
 130  
 131      /**
 132       * Converts a BigInteger to a byte string (eg. base-256).
 133       *
 134       * @param bool $twos_compliment
 135       * @return string
 136       */
 137      public function toBytes($twos_compliment = false)
 138      {
 139          if ($twos_compliment) {
 140              return $this->toBytesHelper();
 141          }
 142  
 143          $value = '';
 144          $current = $this->value;
 145  
 146          if ($current[0] == '-') {
 147              $current = substr($current, 1);
 148          }
 149  
 150          while (bccomp($current, '0', 0) > 0) {
 151              $temp = bcmod($current, '16777216');
 152              $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
 153              $current = bcdiv($current, '16777216', 0);
 154          }
 155  
 156          return $this->precision > 0 ?
 157              substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
 158              ltrim($value, chr(0));
 159      }
 160  
 161      /**
 162       * Adds two BigIntegers.
 163       *
 164       * @param BCMath $y
 165       * @return BCMath
 166       */
 167      public function add(BCMath $y)
 168      {
 169          $temp = new self();
 170          $temp->value = bcadd($this->value, $y->value);
 171  
 172          return $this->normalize($temp);
 173      }
 174  
 175      /**
 176       * Subtracts two BigIntegers.
 177       *
 178       * @param BCMath $y
 179       * @return BCMath
 180       */
 181      public function subtract(BCMath $y)
 182      {
 183          $temp = new self();
 184          $temp->value = bcsub($this->value, $y->value);
 185  
 186          return $this->normalize($temp);
 187      }
 188  
 189      /**
 190       * Multiplies two BigIntegers.
 191       *
 192       * @param BCMath $x
 193       * @return BCMath
 194       */
 195      public function multiply(BCMath $x)
 196      {
 197          $temp = new self();
 198          $temp->value = bcmul($this->value, $x->value);
 199  
 200          return $this->normalize($temp);
 201      }
 202  
 203      /**
 204       * Divides two BigIntegers.
 205       *
 206       * Returns an array whose first element contains the quotient and whose second element contains the
 207       * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
 208       * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
 209       * and the divisor (basically, the "common residue" is the first positive modulo).
 210       *
 211       * @param BCMath $y
 212       * @return array{static, static}
 213       */
 214      public function divide(BCMath $y)
 215      {
 216          $quotient = new self();
 217          $remainder = new self();
 218  
 219          $quotient->value = bcdiv($this->value, $y->value, 0);
 220          $remainder->value = bcmod($this->value, $y->value);
 221  
 222          if ($remainder->value[0] == '-') {
 223              $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
 224          }
 225  
 226          return [$this->normalize($quotient), $this->normalize($remainder)];
 227      }
 228  
 229      /**
 230       * Calculates modular inverses.
 231       *
 232       * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
 233       *
 234       * @param BCMath $n
 235       * @return false|BCMath
 236       */
 237      public function modInverse(BCMath $n)
 238      {
 239          return $this->modInverseHelper($n);
 240      }
 241  
 242      /**
 243       * Calculates the greatest common divisor and Bezout's identity.
 244       *
 245       * Say you have 693 and 609.  The GCD is 21.  Bezout's identity states that there exist integers x and y such that
 246       * 693*x + 609*y == 21.  In point of fact, there are actually an infinite number of x and y combinations and which
 247       * combination is returned is dependent upon which mode is in use.  See
 248       * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
 249       *
 250       * @param BCMath $n
 251       * @return array{gcd: static, x: static, y: static}
 252       */
 253      public function extendedGCD(BCMath $n)
 254      {
 255          // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
 256          // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway.  as is,
 257          // the basic extended euclidean algorithim is what we're using.
 258  
 259          $u = $this->value;
 260          $v = $n->value;
 261  
 262          $a = '1';
 263          $b = '0';
 264          $c = '0';
 265          $d = '1';
 266  
 267          while (bccomp($v, '0', 0) != 0) {
 268              $q = bcdiv($u, $v, 0);
 269  
 270              $temp = $u;
 271              $u = $v;
 272              $v = bcsub($temp, bcmul($v, $q, 0), 0);
 273  
 274              $temp = $a;
 275              $a = $c;
 276              $c = bcsub($temp, bcmul($a, $q, 0), 0);
 277  
 278              $temp = $b;
 279              $b = $d;
 280              $d = bcsub($temp, bcmul($b, $q, 0), 0);
 281          }
 282  
 283          return [
 284              'gcd' => $this->normalize(new static($u)),
 285              'x' => $this->normalize(new static($a)),
 286              'y' => $this->normalize(new static($b))
 287          ];
 288      }
 289  
 290      /**
 291       * Calculates the greatest common divisor
 292       *
 293       * Say you have 693 and 609.  The GCD is 21.
 294       *
 295       * @param BCMath $n
 296       * @return BCMath
 297       */
 298      public function gcd(BCMath $n)
 299      {
 300          extract($this->extendedGCD($n));
 301          /** @var BCMath $gcd */
 302          return $gcd;
 303      }
 304  
 305      /**
 306       * Absolute value.
 307       *
 308       * @return BCMath
 309       */
 310      public function abs()
 311      {
 312          $temp = new static();
 313          $temp->value = strlen($this->value) && $this->value[0] == '-' ?
 314              substr($this->value, 1) :
 315              $this->value;
 316  
 317          return $temp;
 318      }
 319  
 320      /**
 321       * Logical And
 322       *
 323       * @param BCMath $x
 324       * @return BCMath
 325       */
 326      public function bitwise_and(BCMath $x)
 327      {
 328          return $this->bitwiseAndHelper($x);
 329      }
 330  
 331      /**
 332       * Logical Or
 333       *
 334       * @param BCMath $x
 335       * @return BCMath
 336       */
 337      public function bitwise_or(BCMath $x)
 338      {
 339          return $this->bitwiseXorHelper($x);
 340      }
 341  
 342      /**
 343       * Logical Exclusive Or
 344       *
 345       * @param BCMath $x
 346       * @return BCMath
 347       */
 348      public function bitwise_xor(BCMath $x)
 349      {
 350          return $this->bitwiseXorHelper($x);
 351      }
 352  
 353      /**
 354       * Logical Right Shift
 355       *
 356       * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
 357       *
 358       * @param int $shift
 359       * @return BCMath
 360       */
 361      public function bitwise_rightShift($shift)
 362      {
 363          $temp = new static();
 364          $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
 365  
 366          return $this->normalize($temp);
 367      }
 368  
 369      /**
 370       * Logical Left Shift
 371       *
 372       * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
 373       *
 374       * @param int $shift
 375       * @return BCMath
 376       */
 377      public function bitwise_leftShift($shift)
 378      {
 379          $temp = new static();
 380          $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
 381  
 382          return $this->normalize($temp);
 383      }
 384  
 385      /**
 386       * Compares two numbers.
 387       *
 388       * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this
 389       * is demonstrated thusly:
 390       *
 391       * $x  > $y: $x->compare($y)  > 0
 392       * $x  < $y: $x->compare($y)  < 0
 393       * $x == $y: $x->compare($y) == 0
 394       *
 395       * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
 396       *
 397       * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
 398       *
 399       * @param BCMath $y
 400       * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
 401       * @see self::equals()
 402       */
 403      public function compare(BCMath $y)
 404      {
 405          return bccomp($this->value, $y->value, 0);
 406      }
 407  
 408      /**
 409       * Tests the equality of two numbers.
 410       *
 411       * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
 412       *
 413       * @param BCMath $x
 414       * @return bool
 415       */
 416      public function equals(BCMath $x)
 417      {
 418          return $this->value == $x->value;
 419      }
 420  
 421      /**
 422       * Performs modular exponentiation.
 423       *
 424       * @param BCMath $e
 425       * @param BCMath $n
 426       * @return BCMath
 427       */
 428      public function modPow(BCMath $e, BCMath $n)
 429      {
 430          return $this->powModOuter($e, $n);
 431      }
 432  
 433      /**
 434       * Performs modular exponentiation.
 435       *
 436       * Alias for modPow().
 437       *
 438       * @param BCMath $e
 439       * @param BCMath $n
 440       * @return BCMath
 441       */
 442      public function powMod(BCMath $e, BCMath $n)
 443      {
 444          return $this->powModOuter($e, $n);
 445      }
 446  
 447      /**
 448       * Performs modular exponentiation.
 449       *
 450       * @param BCMath $e
 451       * @param BCMath $n
 452       * @return BCMath
 453       */
 454      protected function powModInner(BCMath $e, BCMath $n)
 455      {
 456          try {
 457              $class = static::$modexpEngine[static::class];
 458              return $class::powModHelper($this, $e, $n, static::class);
 459          } catch (\Exception $err) {
 460              return BCMath\DefaultEngine::powModHelper($this, $e, $n, static::class);
 461          }
 462      }
 463  
 464      /**
 465       * Normalize
 466       *
 467       * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
 468       *
 469       * @param BCMath $result
 470       * @return BCMath
 471       */
 472      protected function normalize(BCMath $result)
 473      {
 474          $result->precision = $this->precision;
 475          $result->bitmask = $this->bitmask;
 476  
 477          if ($result->bitmask !== false) {
 478              $result->value = bcmod($result->value, $result->bitmask->value);
 479          }
 480  
 481          return $result;
 482      }
 483  
 484      /**
 485       * Generate a random prime number between a range
 486       *
 487       * If there's not a prime within the given range, false will be returned.
 488       *
 489       * @param BCMath $min
 490       * @param BCMath $max
 491       * @return false|BCMath
 492       */
 493      public static function randomRangePrime(BCMath $min, BCMath $max)
 494      {
 495          return self::randomRangePrimeOuter($min, $max);
 496      }
 497  
 498      /**
 499       * Generate a random number between a range
 500       *
 501       * Returns a random number between $min and $max where $min and $max
 502       * can be defined using one of the two methods:
 503       *
 504       * BigInteger::randomRange($min, $max)
 505       * BigInteger::randomRange($max, $min)
 506       *
 507       * @param BCMath $min
 508       * @param BCMath $max
 509       * @return BCMath
 510       */
 511      public static function randomRange(BCMath $min, BCMath $max)
 512      {
 513          return self::randomRangeHelper($min, $max);
 514      }
 515  
 516      /**
 517       * Make the current number odd
 518       *
 519       * If the current number is odd it'll be unchanged.  If it's even, one will be added to it.
 520       *
 521       * @see self::randomPrime()
 522       */
 523      protected function make_odd()
 524      {
 525          if (!$this->isOdd()) {
 526              $this->value = bcadd($this->value, '1');
 527          }
 528      }
 529  
 530      /**
 531       * Test the number against small primes.
 532       *
 533       * @see self::isPrime()
 534       */
 535      protected function testSmallPrimes()
 536      {
 537          if ($this->value === '1') {
 538              return false;
 539          }
 540          if ($this->value === '2') {
 541              return true;
 542          }
 543          if ($this->value[strlen($this->value) - 1] % 2 == 0) {
 544              return false;
 545          }
 546  
 547          $value = $this->value;
 548  
 549          foreach (self::PRIMES as $prime) {
 550              $r = bcmod($this->value, $prime);
 551              if ($r == '0') {
 552                  return $this->value == $prime;
 553              }
 554          }
 555  
 556          return true;
 557      }
 558  
 559      /**
 560       * Scan for 1 and right shift by that amount
 561       *
 562       * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
 563       *
 564       * @param BCMath $r
 565       * @return int
 566       * @see self::isPrime()
 567       */
 568      public static function scan1divide(BCMath $r)
 569      {
 570          $r_value = &$r->value;
 571          $s = 0;
 572          // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals(static::$one[static::class]) check earlier
 573          while ($r_value[strlen($r_value) - 1] % 2 == 0) {
 574              $r_value = bcdiv($r_value, '2', 0);
 575              ++$s;
 576          }
 577  
 578          return $s;
 579      }
 580  
 581      /**
 582       * Performs exponentiation.
 583       *
 584       * @param BCMath $n
 585       * @return BCMath
 586       */
 587      public function pow(BCMath $n)
 588      {
 589          $temp = new self();
 590          $temp->value = bcpow($this->value, $n->value);
 591  
 592          return $this->normalize($temp);
 593      }
 594  
 595      /**
 596       * Return the minimum BigInteger between an arbitrary number of BigIntegers.
 597       *
 598       * @param BCMath ...$nums
 599       * @return BCMath
 600       */
 601      public static function min(BCMath ...$nums)
 602      {
 603          return self::minHelper($nums);
 604      }
 605  
 606      /**
 607       * Return the maximum BigInteger between an arbitrary number of BigIntegers.
 608       *
 609       * @param BCMath ...$nums
 610       * @return BCMath
 611       */
 612      public static function max(BCMath ...$nums)
 613      {
 614          return self::maxHelper($nums);
 615      }
 616  
 617      /**
 618       * Tests BigInteger to see if it is between two integers, inclusive
 619       *
 620       * @param BCMath $min
 621       * @param BCMath $max
 622       * @return bool
 623       */
 624      public function between(BCMath $min, BCMath $max)
 625      {
 626          return $this->compare($min) >= 0 && $this->compare($max) <= 0;
 627      }
 628  
 629      /**
 630       * Set Bitmask
 631       *
 632       * @param int $bits
 633       * @return Engine
 634       * @see self::setPrecision()
 635       */
 636      protected static function setBitmask($bits)
 637      {
 638          $temp = parent::setBitmask($bits);
 639          return $temp->add(static::$one[static::class]);
 640      }
 641  
 642      /**
 643       * Is Odd?
 644       *
 645       * @return bool
 646       */
 647      public function isOdd()
 648      {
 649          return $this->value[strlen($this->value) - 1] % 2 == 1;
 650      }
 651  
 652      /**
 653       * Tests if a bit is set
 654       *
 655       * @return bool
 656       */
 657      public function testBit($x)
 658      {
 659          return bccomp(
 660              bcmod($this->value, bcpow('2', $x + 1, 0)),
 661              bcpow('2', $x, 0),
 662              0
 663          ) >= 0;
 664      }
 665  
 666      /**
 667       * Is Negative?
 668       *
 669       * @return bool
 670       */
 671      public function isNegative()
 672      {
 673          return strlen($this->value) && $this->value[0] == '-';
 674      }
 675  
 676      /**
 677       * Negate
 678       *
 679       * Given $k, returns -$k
 680       *
 681       * @return BCMath
 682       */
 683      public function negate()
 684      {
 685          $temp = clone $this;
 686  
 687          if (!strlen($temp->value)) {
 688              return $temp;
 689          }
 690  
 691          $temp->value = $temp->value[0] == '-' ?
 692              substr($this->value, 1) :
 693              '-' . $this->value;
 694  
 695          return $temp;
 696      }
 697  }