fork(1) download
  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**#@+*/
  6. define('MATH_BIGINTEGER_MONTGOMERY', 0);
  7. define('MATH_BIGINTEGER_BARRETT', 1);
  8. define('MATH_BIGINTEGER_POWEROF2', 2);
  9. define('MATH_BIGINTEGER_CLASSIC', 3);
  10. define('MATH_BIGINTEGER_NONE', 4);
  11. /**#@-*/
  12.  
  13. /**#@+*/
  14. define('MATH_BIGINTEGER_VALUE', 0);
  15. define('MATH_BIGINTEGER_SIGN', 1);
  16. /**#@-*/
  17.  
  18. /**#@+*/
  19. define('MATH_BIGINTEGER_VARIABLE', 0);
  20. define('MATH_BIGINTEGER_DATA', 1);
  21. /**#@-*/
  22.  
  23. /**#@+*/
  24. define('MATH_BIGINTEGER_MODE_INTERNAL', 1);
  25.  
  26. define('MATH_BIGINTEGER_MODE_BCMATH', 2);
  27.  
  28. define('MATH_BIGINTEGER_MODE_GMP', 3);
  29. /**#@-*/
  30.  
  31. define('MATH_BIGINTEGER_KARATSUBA_CUTOFF', 25);
  32.  
  33. class Math_BigInteger {
  34. var $value;
  35.  
  36. var $is_negative = false;
  37.  
  38. var $precision = -1;
  39.  
  40. var $bitmask = false;
  41.  
  42. var $hex;
  43.  
  44. function Math_BigInteger($x = 0, $base = 10)
  45. {
  46. if ( !defined('MATH_BIGINTEGER_MODE') ) {
  47. switch (true) {
  48. case extension_loaded('gmp'):
  49. define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_GMP);
  50. break;
  51. case extension_loaded('bcmath'):
  52. define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_BCMATH);
  53. break;
  54. default:
  55. define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_INTERNAL);
  56. }
  57. }
  58.  
  59. if (function_exists('openssl_public_encrypt') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
  60. define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
  61. }
  62.  
  63. if (!defined('PHP_INT_SIZE')) {
  64. define('PHP_INT_SIZE', 4);
  65. }
  66.  
  67. if (!defined('MATH_BIGINTEGER_BASE') && MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_INTERNAL) {
  68. switch (PHP_INT_SIZE) {
  69. case 8: // use 64-bit integers if int size is 8 bytes
  70. define('MATH_BIGINTEGER_BASE', 31);
  71. define('MATH_BIGINTEGER_BASE_FULL', 0x80000000);
  72. define('MATH_BIGINTEGER_MAX_DIGIT', 0x7FFFFFFF);
  73. define('MATH_BIGINTEGER_MSB', 0x40000000);
  74. define('MATH_BIGINTEGER_MAX10', 1000000000);
  75. define('MATH_BIGINTEGER_MAX10_LEN', 9);
  76. define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 62));
  77. break;
  78. default:
  79. define('MATH_BIGINTEGER_BASE', 26);
  80. define('MATH_BIGINTEGER_BASE_FULL', 0x4000000);
  81. define('MATH_BIGINTEGER_MAX_DIGIT', 0x3FFFFFF);
  82. define('MATH_BIGINTEGER_MSB', 0x2000000);
  83. define('MATH_BIGINTEGER_MAX10', 10000000);
  84. define('MATH_BIGINTEGER_MAX10_LEN', 7);
  85. define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 52));
  86. }
  87. }
  88.  
  89. switch ( MATH_BIGINTEGER_MODE ) {
  90. case MATH_BIGINTEGER_MODE_GMP:
  91. if (is_resource($x) && get_resource_type($x) == 'GMP integer') {
  92. $this->value = $x;
  93. return;
  94. }
  95. $this->value = gmp_init(0);
  96. break;
  97. case MATH_BIGINTEGER_MODE_BCMATH:
  98. $this->value = '0';
  99. break;
  100. default:
  101. $this->value = array();
  102. }
  103.  
  104. if (empty($x) && (abs($base) != 256 || $x !== '0')) {
  105. return;
  106. }
  107.  
  108. switch ($base) {
  109. case -256:
  110. if (ord($x[0]) & 0x80) {
  111. $x = ~$x;
  112. $this->is_negative = true;
  113. }
  114. case 256:
  115. switch ( MATH_BIGINTEGER_MODE ) {
  116. case MATH_BIGINTEGER_MODE_GMP:
  117. $sign = $this->is_negative ? '-' : '';
  118. $this->value = gmp_init($sign . '0x' . bin2hex($x));
  119. break;
  120. case MATH_BIGINTEGER_MODE_BCMATH:
  121. $len = (strlen($x) + 3) & 0xFFFFFFFC;
  122.  
  123. $x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
  124.  
  125. for ($i = 0; $i < $len; $i+= 4) {
  126. $this->value = bcmul($this->value, '4294967296', 0);
  127. $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
  128. }
  129.  
  130. if ($this->is_negative) {
  131. $this->value = '-' . $this->value;
  132. }
  133.  
  134. break;
  135. default:
  136. while (strlen($x)) {
  137. $this->value[] = $this->_bytes2int($this->_base256_rshift($x, MATH_BIGINTEGER_BASE));
  138. }
  139. }
  140.  
  141. if ($this->is_negative) {
  142. if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) {
  143. $this->is_negative = false;
  144. }
  145. $temp = $this->add(new Math_BigInteger('-1'));
  146. $this->value = $temp->value;
  147. }
  148. break;
  149. case 16:
  150. case -16:
  151. if ($base > 0 && $x[0] == '-') {
  152. $this->is_negative = true;
  153. $x = substr($x, 1);
  154. }
  155.  
  156. $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
  157.  
  158. $is_negative = false;
  159. if ($base < 0 && hexdec($x[0]) >= 8) {
  160. $this->is_negative = $is_negative = true;
  161. $x = bin2hex(~pack('H*', $x));
  162. }
  163.  
  164. switch ( MATH_BIGINTEGER_MODE ) {
  165. case MATH_BIGINTEGER_MODE_GMP:
  166. $temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
  167. $this->value = gmp_init($temp);
  168. $this->is_negative = false;
  169. break;
  170. case MATH_BIGINTEGER_MODE_BCMATH:
  171. $x = ( strlen($x) & 1 ) ? '0' . $x : $x;
  172. $temp = new Math_BigInteger(pack('H*', $x), 256);
  173. $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
  174. $this->is_negative = false;
  175. break;
  176. default:
  177. $x = ( strlen($x) & 1 ) ? '0' . $x : $x;
  178. $temp = new Math_BigInteger(pack('H*', $x), 256);
  179. $this->value = $temp->value;
  180. }
  181.  
  182. if ($is_negative) {
  183. $temp = $this->add(new Math_BigInteger('-1'));
  184. $this->value = $temp->value;
  185. }
  186. break;
  187. case 10:
  188. case -10:
  189. $x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x);
  190.  
  191. switch ( MATH_BIGINTEGER_MODE ) {
  192. case MATH_BIGINTEGER_MODE_GMP:
  193. $this->value = gmp_init($x);
  194. break;
  195. case MATH_BIGINTEGER_MODE_BCMATH:
  196. $this->value = $x === '-' ? '0' : (string) $x;
  197. break;
  198. default:
  199. $temp = new Math_BigInteger();
  200.  
  201. $multiplier = new Math_BigInteger();
  202. $multiplier->value = array(MATH_BIGINTEGER_MAX10);
  203.  
  204. if ($x[0] == '-') {
  205. $this->is_negative = true;
  206. $x = substr($x, 1);
  207. }
  208.  
  209. $x = str_pad($x, strlen($x) + ((MATH_BIGINTEGER_MAX10_LEN - 1) * strlen($x)) % MATH_BIGINTEGER_MAX10_LEN, 0, STR_PAD_LEFT);
  210.  
  211. while (strlen($x)) {
  212. $temp = $temp->multiply($multiplier);
  213. $temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, MATH_BIGINTEGER_MAX10_LEN)), 256));
  214. $x = substr($x, MATH_BIGINTEGER_MAX10_LEN);
  215. }
  216.  
  217. $this->value = $temp->value;
  218. }
  219. break;
  220. case 2:
  221. case -2:
  222. if ($base > 0 && $x[0] == '-') {
  223. $this->is_negative = true;
  224. $x = substr($x, 1);
  225. }
  226.  
  227. $x = preg_replace('#^([01]*).*#', '$1', $x);
  228. $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
  229.  
  230. $str = '0x';
  231. while (strlen($x)) {
  232. $part = substr($x, 0, 4);
  233. $str.= dechex(bindec($part));
  234. $x = substr($x, 4);
  235. }
  236.  
  237. if ($this->is_negative) {
  238. $str = '-' . $str;
  239. }
  240.  
  241. $temp = new Math_BigInteger($str, 8 * $base); // ie. either -16 or +16
  242. $this->value = $temp->value;
  243. $this->is_negative = $temp->is_negative;
  244.  
  245. break;
  246. default:
  247. // base not supported, so we'll let $this == 0
  248. }
  249. }
  250.  
  251. function toBytes($twos_compliment = false) {
  252. if ($twos_compliment) {
  253. $comparison = $this->compare(new Math_BigInteger());
  254. if ($comparison == 0) {
  255. return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
  256. }
  257.  
  258. $temp = $comparison < 0 ? $this->add(new Math_BigInteger(1)) : $this->copy();
  259. $bytes = $temp->toBytes();
  260.  
  261. if (empty($bytes)) {
  262. $bytes = chr(0);
  263. }
  264.  
  265. if (ord($bytes[0]) & 0x80) {
  266. $bytes = chr(0) . $bytes;
  267. }
  268.  
  269. return $comparison < 0 ? ~$bytes : $bytes;
  270. }
  271.  
  272. switch ( MATH_BIGINTEGER_MODE ) {
  273. case MATH_BIGINTEGER_MODE_GMP:
  274. if (gmp_cmp($this->value, gmp_init(0)) == 0) {
  275. return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
  276. }
  277.  
  278. $temp = gmp_strval(gmp_abs($this->value), 16);
  279. $temp = ( strlen($temp) & 1 ) ? '0' . $temp : $temp;
  280. $temp = pack('H*', $temp);
  281.  
  282. return $this->precision > 0 ?
  283. substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
  284. ltrim($temp, chr(0));
  285. case MATH_BIGINTEGER_MODE_BCMATH:
  286. if ($this->value === '0') {
  287. return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
  288. }
  289.  
  290. $value = '';
  291. $current = $this->value;
  292.  
  293. if ($current[0] == '-') {
  294. $current = substr($current, 1);
  295. }
  296.  
  297. while (bccomp($current, '0', 0) > 0) {
  298. $temp = bcmod($current, '16777216');
  299. $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
  300. $current = bcdiv($current, '16777216', 0);
  301. }
  302.  
  303. return $this->precision > 0 ?
  304. substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
  305. ltrim($value, chr(0));
  306. }
  307.  
  308. if (!count($this->value)) {
  309. return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
  310. }
  311. $result = $this->_int2bytes($this->value[count($this->value) - 1]);
  312.  
  313. $temp = $this->copy();
  314.  
  315. for ($i = count($temp->value) - 2; $i >= 0; --$i) {
  316. $temp->_base256_lshift($result, MATH_BIGINTEGER_BASE);
  317. $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
  318. }
  319.  
  320. return $this->precision > 0 ?
  321. str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
  322. $result;
  323. }
  324.  
  325. function toHex($twos_compliment = false) {
  326. return bin2hex($this->toBytes($twos_compliment));
  327. }
  328.  
  329. function toBits($twos_compliment = false) {
  330. $hex = $this->toHex($twos_compliment);
  331. $bits = '';
  332. for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) {
  333. $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits;
  334. }
  335. if ($start) {
  336. $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits;
  337. }
  338. $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
  339.  
  340. if ($twos_compliment && $this->compare(new Math_BigInteger()) > 0 && $this->precision <= 0) {
  341. return '0' . $result;
  342. }
  343.  
  344. return $result;
  345. }
  346.  
  347. function toString() {
  348. switch ( MATH_BIGINTEGER_MODE ) {
  349. case MATH_BIGINTEGER_MODE_GMP:
  350. return gmp_strval($this->value);
  351. case MATH_BIGINTEGER_MODE_BCMATH:
  352. if ($this->value === '0') {
  353. return '0';
  354. }
  355.  
  356. return ltrim($this->value, '0');
  357. }
  358.  
  359. if (!count($this->value)) {
  360. return '0';
  361. }
  362.  
  363. $temp = $this->copy();
  364. $temp->is_negative = false;
  365.  
  366. $divisor = new Math_BigInteger();
  367. $divisor->value = array(MATH_BIGINTEGER_MAX10);
  368. $result = '';
  369. while (count($temp->value)) {
  370. list($temp, $mod) = $temp->divide($divisor);
  371. $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', MATH_BIGINTEGER_MAX10_LEN, '0', STR_PAD_LEFT) . $result;
  372. }
  373. $result = ltrim($result, '0');
  374. if (empty($result)) {
  375. $result = '0';
  376. }
  377.  
  378. if ($this->is_negative) {
  379. $result = '-' . $result;
  380. }
  381.  
  382. return $result;
  383. }
  384.  
  385. function copy() {
  386. $temp = new Math_BigInteger();
  387. $temp->value = $this->value;
  388. $temp->is_negative = $this->is_negative;
  389. $temp->precision = $this->precision;
  390. $temp->bitmask = $this->bitmask;
  391. return $temp;
  392. }
  393.  
  394. function __toString() {
  395. return $this->toString();
  396. }
  397.  
  398. function __clone() {
  399. return $this->copy();
  400. }
  401.  
  402. function __sleep() {
  403. $this->hex = $this->toHex(true);
  404. $vars = array('hex');
  405. if ($this->precision > 0) {
  406. $vars[] = 'precision';
  407. }
  408. return $vars;
  409.  
  410. }
  411.  
  412. function __wakeup() {
  413. $temp = new Math_BigInteger($this->hex, -16);
  414. $this->value = $temp->value;
  415. $this->is_negative = $temp->is_negative;
  416. if ($this->precision > 0) {
  417. $this->setPrecision($this->precision);
  418. }
  419. }
  420.  
  421. function add($y) {
  422. switch ( MATH_BIGINTEGER_MODE ) {
  423. case MATH_BIGINTEGER_MODE_GMP:
  424. $temp = new Math_BigInteger();
  425. $temp->value = gmp_add($this->value, $y->value);
  426.  
  427. return $this->_normalize($temp);
  428. case MATH_BIGINTEGER_MODE_BCMATH:
  429. $temp = new Math_BigInteger();
  430. $temp->value = bcadd($this->value, $y->value, 0);
  431.  
  432. return $this->_normalize($temp);
  433. }
  434.  
  435. $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative);
  436.  
  437. $result = new Math_BigInteger();
  438. $result->value = $temp[MATH_BIGINTEGER_VALUE];
  439. $result->is_negative = $temp[MATH_BIGINTEGER_SIGN];
  440.  
  441. return $this->_normalize($result);
  442. }
  443.  
  444. function _add($x_value, $x_negative, $y_value, $y_negative) {
  445. $x_size = count($x_value);
  446. $y_size = count($y_value);
  447.  
  448. if ($x_size == 0) {
  449. return array(
  450. MATH_BIGINTEGER_VALUE => $y_value,
  451. MATH_BIGINTEGER_SIGN => $y_negative
  452. );
  453. } else if ($y_size == 0) {
  454. return array(
  455. MATH_BIGINTEGER_VALUE => $x_value,
  456. MATH_BIGINTEGER_SIGN => $x_negative
  457. );
  458. }
  459.  
  460. if ( $x_negative != $y_negative ) {
  461. if ( $x_value == $y_value ) {
  462. return array(
  463. MATH_BIGINTEGER_VALUE => array(),
  464. MATH_BIGINTEGER_SIGN => false
  465. );
  466. }
  467.  
  468. $temp = $this->_subtract($x_value, false, $y_value, false);
  469. $temp[MATH_BIGINTEGER_SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ? $x_negative : $y_negative;
  470.  
  471. return $temp;
  472. }
  473.  
  474. if ($x_size < $y_size) {
  475. $size = $x_size;
  476. $value = $y_value;
  477. } else {
  478. $size = $y_size;
  479. $value = $x_value;
  480. }
  481.  
  482. $value[] = 0;
  483.  
  484. $carry = 0;
  485. for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
  486. $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] + $y_value[$j] * MATH_BIGINTEGER_BASE_FULL + $y_value[$i] + $carry;
  487. $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT2;
  488. $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT2 : $sum;
  489.  
  490. $temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL);
  491.  
  492. $value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp);
  493. $value[$j] = $temp;
  494. }
  495.  
  496. if ($j == $size) {
  497. $sum = $x_value[$i] + $y_value[$i] + $carry;
  498. $carry = $sum >= MATH_BIGINTEGER_BASE_FULL;
  499. $value[$i] = $carry ? $sum - MATH_BIGINTEGER_BASE_FULL : $sum;
  500. ++$i;
  501. }
  502.  
  503. if ($carry) {
  504. for (; $value[$i] == MATH_BIGINTEGER_MAX_DIGIT; ++$i) {
  505. $value[$i] = 0;
  506. }
  507. ++$value[$i];
  508. }
  509.  
  510. return array(
  511. MATH_BIGINTEGER_VALUE => $this->_trim($value),
  512. MATH_BIGINTEGER_SIGN => $x_negative
  513. );
  514. }
  515.  
  516. function subtract($y) {
  517. switch ( MATH_BIGINTEGER_MODE ) {
  518. case MATH_BIGINTEGER_MODE_GMP:
  519. $temp = new Math_BigInteger();
  520. $temp->value = gmp_sub($this->value, $y->value);
  521.  
  522. return $this->_normalize($temp);
  523. case MATH_BIGINTEGER_MODE_BCMATH:
  524. $temp = new Math_BigInteger();
  525. $temp->value = bcsub($this->value, $y->value, 0);
  526.  
  527. return $this->_normalize($temp);
  528. }
  529.  
  530. $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);
  531.  
  532. $result = new Math_BigInteger();
  533. $result->value = $temp[MATH_BIGINTEGER_VALUE];
  534. $result->is_negative = $temp[MATH_BIGINTEGER_SIGN];
  535.  
  536. return $this->_normalize($result);
  537. }
  538.  
  539. function _subtract($x_value, $x_negative, $y_value, $y_negative) {
  540. $x_size = count($x_value);
  541. $y_size = count($y_value);
  542.  
  543. if ($x_size == 0) {
  544. return array(
  545. MATH_BIGINTEGER_VALUE => $y_value,
  546. MATH_BIGINTEGER_SIGN => !$y_negative
  547. );
  548. } else if ($y_size == 0) {
  549. return array(
  550. MATH_BIGINTEGER_VALUE => $x_value,
  551. MATH_BIGINTEGER_SIGN => $x_negative
  552. );
  553. }
  554.  
  555. if ( $x_negative != $y_negative ) {
  556. $temp = $this->_add($x_value, false, $y_value, false);
  557. $temp[MATH_BIGINTEGER_SIGN] = $x_negative;
  558.  
  559. return $temp;
  560. }
  561.  
  562. $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative);
  563.  
  564. if ( !$diff ) {
  565. return array(
  566. MATH_BIGINTEGER_VALUE => array(),
  567. MATH_BIGINTEGER_SIGN => false
  568. );
  569. }
  570.  
  571. if ( (!$x_negative && $diff < 0) || ($x_negative && $diff > 0) ) {
  572. $temp = $x_value;
  573. $x_value = $y_value;
  574. $y_value = $temp;
  575.  
  576. $x_negative = !$x_negative;
  577.  
  578. $x_size = count($x_value);
  579. $y_size = count($y_value);
  580. }
  581.  
  582. $carry = 0;
  583. for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
  584. $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] - $y_value[$j] * MATH_BIGINTEGER_BASE_FULL - $y_value[$i] - $carry;
  585. $carry = $sum < 0;
  586. $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT2 : $sum;
  587.  
  588. $temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL);
  589.  
  590. $x_value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp);
  591. $x_value[$j] = $temp;
  592. }
  593.  
  594. if ($j == $y_size) {
  595. $sum = $x_value[$i] - $y_value[$i] - $carry;
  596. $carry = $sum < 0;
  597. $x_value[$i] = $carry ? $sum + MATH_BIGINTEGER_BASE_FULL : $sum;
  598. ++$i;
  599. }
  600.  
  601. if ($carry) {
  602. for (; !$x_value[$i]; ++$i) {
  603. $x_value[$i] = MATH_BIGINTEGER_MAX_DIGIT;
  604. }
  605. --$x_value[$i];
  606. }
  607.  
  608. return array(
  609. MATH_BIGINTEGER_VALUE => $this->_trim($x_value),
  610. MATH_BIGINTEGER_SIGN => $x_negative
  611. );
  612. }
  613.  
  614. function multiply($x) {
  615. switch ( MATH_BIGINTEGER_MODE ) {
  616. case MATH_BIGINTEGER_MODE_GMP:
  617. $temp = new Math_BigInteger();
  618. $temp->value = gmp_mul($this->value, $x->value);
  619.  
  620. return $this->_normalize($temp);
  621. case MATH_BIGINTEGER_MODE_BCMATH:
  622. $temp = new Math_BigInteger();
  623. $temp->value = bcmul($this->value, $x->value, 0);
  624.  
  625. return $this->_normalize($temp);
  626. }
  627.  
  628. $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);
  629.  
  630. $product = new Math_BigInteger();
  631. $product->value = $temp[MATH_BIGINTEGER_VALUE];
  632. $product->is_negative = $temp[MATH_BIGINTEGER_SIGN];
  633.  
  634. return $this->_normalize($product);
  635. }
  636.  
  637. function _multiply($x_value, $x_negative, $y_value, $y_negative) {
  638. $x_length = count($x_value);
  639. $y_length = count($y_value);
  640.  
  641. if ( !$x_length || !$y_length ) {
  642. return array(
  643. MATH_BIGINTEGER_VALUE => array(),
  644. MATH_BIGINTEGER_SIGN => false
  645. );
  646. }
  647.  
  648. return array(
  649. MATH_BIGINTEGER_VALUE => min($x_length, $y_length) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
  650. $this->_trim($this->_regularMultiply($x_value, $y_value)) :
  651. $this->_trim($this->_karatsuba($x_value, $y_value)),
  652. MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
  653. );
  654. }
  655.  
  656. function _regularMultiply($x_value, $y_value) {
  657. $x_length = count($x_value);
  658. $y_length = count($y_value);
  659.  
  660. if ( !$x_length || !$y_length ) {
  661. return array();
  662. }
  663.  
  664. if ( $x_length < $y_length ) {
  665. $temp = $x_value;
  666. $x_value = $y_value;
  667. $y_value = $temp;
  668.  
  669. $x_length = count($x_value);
  670. $y_length = count($y_value);
  671. }
  672.  
  673. $product_value = $this->_array_repeat(0, $x_length + $y_length);
  674.  
  675. $carry = 0;
  676.  
  677. for ($j = 0; $j < $x_length; ++$j) {
  678. $temp = $x_value[$j] * $y_value[0] + $carry;
  679. $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL);
  680. $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
  681. }
  682.  
  683. $product_value[$j] = $carry;
  684.  
  685. for ($i = 1; $i < $y_length; ++$i) {
  686. $carry = 0;
  687.  
  688. for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
  689. $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
  690. $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL);
  691. $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
  692. }
  693.  
  694. $product_value[$k] = $carry;
  695. }
  696.  
  697. return $product_value;
  698. }
  699.  
  700. function _karatsuba($x_value, $y_value) {
  701. $m = min(count($x_value) >> 1, count($y_value) >> 1);
  702.  
  703. if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
  704. return $this->_regularMultiply($x_value, $y_value);
  705. }
  706.  
  707. $x1 = array_slice($x_value, $m);
  708. $x0 = array_slice($x_value, 0, $m);
  709. $y1 = array_slice($y_value, $m);
  710. $y0 = array_slice($y_value, 0, $m);
  711.  
  712. $z2 = $this->_karatsuba($x1, $y1);
  713. $z0 = $this->_karatsuba($x0, $y0);
  714.  
  715. $z1 = $this->_add($x1, false, $x0, false);
  716. $temp = $this->_add($y1, false, $y0, false);
  717. $z1 = $this->_karatsuba($z1[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_VALUE]);
  718. $temp = $this->_add($z2, false, $z0, false);
  719. $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);
  720.  
  721. $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
  722. $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);
  723.  
  724. $xy = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
  725. $xy = $this->_add($xy[MATH_BIGINTEGER_VALUE], $xy[MATH_BIGINTEGER_SIGN], $z0, false);
  726.  
  727. return $xy[MATH_BIGINTEGER_VALUE];
  728. }
  729.  
  730. function _square($x = false) {
  731. return count($x) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
  732. $this->_trim($this->_baseSquare($x)) :
  733. $this->_trim($this->_karatsubaSquare($x));
  734. }
  735.  
  736. function _baseSquare($value) {
  737. if ( empty($value) ) {
  738. return array();
  739. }
  740. $square_value = $this->_array_repeat(0, 2 * count($value));
  741.  
  742. for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
  743. $i2 = $i << 1;
  744.  
  745. $temp = $square_value[$i2] + $value[$i] * $value[$i];
  746. $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL);
  747. $square_value[$i2] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
  748.  
  749. // note how we start from $i+1 instead of 0 as we do in multiplication.
  750. for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
  751. $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
  752. $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL);
  753. $square_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
  754. }
  755.  
  756. // the following line can yield values larger 2**15. at this point, PHP should switch
  757. // over to floats.
  758. $square_value[$i + $max_index + 1] = $carry;
  759. }
  760.  
  761. return $square_value;
  762. }
  763.  
  764. function _karatsubaSquare($value) {
  765. $m = count($value) >> 1;
  766.  
  767. if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
  768. return $this->_baseSquare($value);
  769. }
  770.  
  771. $x1 = array_slice($value, $m);
  772. $x0 = array_slice($value, 0, $m);
  773.  
  774. $z2 = $this->_karatsubaSquare($x1);
  775. $z0 = $this->_karatsubaSquare($x0);
  776.  
  777. $z1 = $this->_add($x1, false, $x0, false);
  778. $z1 = $this->_karatsubaSquare($z1[MATH_BIGINTEGER_VALUE]);
  779. $temp = $this->_add($z2, false, $z0, false);
  780. $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);
  781.  
  782. $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
  783. $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);
  784.  
  785. $xx = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
  786. $xx = $this->_add($xx[MATH_BIGINTEGER_VALUE], $xx[MATH_BIGINTEGER_SIGN], $z0, false);
  787.  
  788. return $xx[MATH_BIGINTEGER_VALUE];
  789. }
  790.  
  791. function divide($y) {
  792. switch ( MATH_BIGINTEGER_MODE ) {
  793. case MATH_BIGINTEGER_MODE_GMP:
  794. $quotient = new Math_BigInteger();
  795. $remainder = new Math_BigInteger();
  796.  
  797. list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
  798.  
  799. if (gmp_sign($remainder->value) < 0) {
  800. $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
  801. }
  802.  
  803. return array($this->_normalize($quotient), $this->_normalize($remainder));
  804. case MATH_BIGINTEGER_MODE_BCMATH:
  805. $quotient = new Math_BigInteger();
  806. $remainder = new Math_BigInteger();
  807.  
  808. $quotient->value = bcdiv($this->value, $y->value, 0);
  809. $remainder->value = bcmod($this->value, $y->value);
  810.  
  811. if ($remainder->value[0] == '-') {
  812. $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
  813. }
  814.  
  815. return array($this->_normalize($quotient), $this->_normalize($remainder));
  816. }
  817.  
  818. if (count($y->value) == 1) {
  819. list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
  820. $quotient = new Math_BigInteger();
  821. $remainder = new Math_BigInteger();
  822. $quotient->value = $q;
  823. $remainder->value = array($r);
  824. $quotient->is_negative = $this->is_negative != $y->is_negative;
  825. return array($this->_normalize($quotient), $this->_normalize($remainder));
  826. }
  827.  
  828. static $zero;
  829. if ( !isset($zero) ) {
  830. $zero = new Math_BigInteger();
  831. }
  832.  
  833. $x = $this->copy();
  834. $y = $y->copy();
  835.  
  836. $x_sign = $x->is_negative;
  837. $y_sign = $y->is_negative;
  838.  
  839. $x->is_negative = $y->is_negative = false;
  840.  
  841. $diff = $x->compare($y);
  842.  
  843. if ( !$diff ) {
  844. $temp = new Math_BigInteger();
  845. $temp->value = array(1);
  846. $temp->is_negative = $x_sign != $y_sign;
  847. return array($this->_normalize($temp), $this->_normalize(new Math_BigInteger()));
  848. }
  849.  
  850. if ( $diff < 0 ) {
  851. // if $x is negative, "add" $y.
  852. if ( $x_sign ) {
  853. $x = $y->subtract($x);
  854. }
  855. return array($this->_normalize(new Math_BigInteger()), $this->_normalize($x));
  856. }
  857.  
  858. // normalize $x and $y as described in HAC 14.23 / 14.24
  859. $msb = $y->value[count($y->value) - 1];
  860. for ($shift = 0; !($msb & MATH_BIGINTEGER_MSB); ++$shift) {
  861. $msb <<= 1;
  862. }
  863. $x->_lshift($shift);
  864. $y->_lshift($shift);
  865. $y_value = &$y->value;
  866.  
  867. $x_max = count($x->value) - 1;
  868. $y_max = count($y->value) - 1;
  869.  
  870. $quotient = new Math_BigInteger();
  871. $quotient_value = &$quotient->value;
  872. $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
  873.  
  874. static $temp, $lhs, $rhs;
  875. if (!isset($temp)) {
  876. $temp = new Math_BigInteger();
  877. $lhs = new Math_BigInteger();
  878. $rhs = new Math_BigInteger();
  879. }
  880. $temp_value = &$temp->value;
  881. $rhs_value = &$rhs->value;
  882.  
  883. // $temp = $y << ($x_max - $y_max-1) in base 2**26
  884. $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);
  885.  
  886. while ( $x->compare($temp) >= 0 ) {
  887. // calculate the "common residue"
  888. ++$quotient_value[$x_max - $y_max];
  889. $x = $x->subtract($temp);
  890. $x_max = count($x->value) - 1;
  891. }
  892.  
  893. for ($i = $x_max; $i >= $y_max + 1; --$i) {
  894. $x_value = &$x->value;
  895. $x_window = array(
  896. isset($x_value[$i]) ? $x_value[$i] : 0,
  897. isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
  898. isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
  899. );
  900. $y_window = array(
  901. $y_value[$y_max],
  902. ( $y_max > 0 ) ? $y_value[$y_max - 1] : 0
  903. );
  904.  
  905. $q_index = $i - $y_max - 1;
  906. if ($x_window[0] == $y_window[0]) {
  907. $quotient_value[$q_index] = MATH_BIGINTEGER_MAX_DIGIT;
  908. } else {
  909. $quotient_value[$q_index] = (int) (
  910. ($x_window[0] * MATH_BIGINTEGER_BASE_FULL + $x_window[1])
  911. /
  912. $y_window[0]
  913. );
  914. }
  915.  
  916. $temp_value = array($y_window[1], $y_window[0]);
  917.  
  918. $lhs->value = array($quotient_value[$q_index]);
  919. $lhs = $lhs->multiply($temp);
  920.  
  921. $rhs_value = array($x_window[2], $x_window[1], $x_window[0]);
  922.  
  923. while ( $lhs->compare($rhs) > 0 ) {
  924. --$quotient_value[$q_index];
  925.  
  926. $lhs->value = array($quotient_value[$q_index]);
  927. $lhs = $lhs->multiply($temp);
  928. }
  929.  
  930. $adjust = $this->_array_repeat(0, $q_index);
  931. $temp_value = array($quotient_value[$q_index]);
  932. $temp = $temp->multiply($y);
  933. $temp_value = &$temp->value;
  934. $temp_value = array_merge($adjust, $temp_value);
  935.  
  936. $x = $x->subtract($temp);
  937.  
  938. if ($x->compare($zero) < 0) {
  939. $temp_value = array_merge($adjust, $y_value);
  940. $x = $x->add($temp);
  941.  
  942. --$quotient_value[$q_index];
  943. }
  944.  
  945. $x_max = count($x_value) - 1;
  946. }
  947.  
  948. $x->_rshift($shift);
  949.  
  950. $quotient->is_negative = $x_sign != $y_sign;
  951.  
  952. if ( $x_sign ) {
  953. $y->_rshift($shift);
  954. $x = $y->subtract($x);
  955. }
  956.  
  957. return array($this->_normalize($quotient), $this->_normalize($x));
  958. }
  959.  
  960. function _divide_digit($dividend, $divisor) {
  961. $carry = 0;
  962. $result = array();
  963.  
  964. for ($i = count($dividend) - 1; $i >= 0; --$i) {
  965. $temp = MATH_BIGINTEGER_BASE_FULL * $carry + $dividend[$i];
  966. $result[$i] = (int) ($temp / $divisor);
  967. $carry = (int) ($temp - $divisor * $result[$i]);
  968. }
  969.  
  970. return array($result, $carry);
  971. }
  972.  
  973. function modPow($e, $n) {
  974. $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
  975.  
  976. if ($e->compare(new Math_BigInteger()) < 0) {
  977. $e = $e->abs();
  978.  
  979. $temp = $this->modInverse($n);
  980. if ($temp === false) {
  981. return false;
  982. }
  983.  
  984. return $this->_normalize($temp->modPow($e, $n));
  985. }
  986.  
  987. if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP ) {
  988. $temp = new Math_BigInteger();
  989. $temp->value = gmp_powm($this->value, $e->value, $n->value);
  990.  
  991. return $this->_normalize($temp);
  992. }
  993.  
  994. if ($this->compare(new Math_BigInteger()) < 0 || $this->compare($n) > 0) {
  995. list(, $temp) = $this->divide($n);
  996. return $temp->modPow($e, $n);
  997. }
  998.  
  999. if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
  1000. $components = array(
  1001. 'modulus' => $n->toBytes(true),
  1002. 'publicExponent' => $e->toBytes(true)
  1003. );
  1004.  
  1005. $components = array(
  1006. 'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']),
  1007. 'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent'])
  1008. );
  1009.  
  1010. $RSAPublicKey = pack('Ca*a*a*',
  1011. 48, $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])),
  1012. $components['modulus'], $components['publicExponent']
  1013. );
  1014.  
  1015. $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
  1016. $RSAPublicKey = chr(0) . $RSAPublicKey;
  1017. $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey;
  1018.  
  1019. $encapsulated = pack('Ca*a*',
  1020. 48, $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey
  1021. );
  1022.  
  1023. $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
  1024. chunk_split(base64_encode($encapsulated)) .
  1025. '-----END PUBLIC KEY-----';
  1026.  
  1027. $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);
  1028.  
  1029. if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) {
  1030. return new Math_BigInteger($result, 256);
  1031. }
  1032. }
  1033.  
  1034. if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
  1035. $temp = new Math_BigInteger();
  1036. $temp->value = bcpowmod($this->value, $e->value, $n->value, 0);
  1037.  
  1038. return $this->_normalize($temp);
  1039. }
  1040.  
  1041. if ( empty($e->value) ) {
  1042. $temp = new Math_BigInteger();
  1043. $temp->value = array(1);
  1044. return $this->_normalize($temp);
  1045. }
  1046.  
  1047. if ( $e->value == array(1) ) {
  1048. list(, $temp) = $this->divide($n);
  1049. return $this->_normalize($temp);
  1050. }
  1051.  
  1052. if ( $e->value == array(2) ) {
  1053. $temp = new Math_BigInteger();
  1054. $temp->value = $this->_square($this->value);
  1055. list(, $temp) = $temp->divide($n);
  1056. return $this->_normalize($temp);
  1057. }
  1058.  
  1059. return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT));
  1060.  
  1061. if ( $n->value[0] & 1 ) {
  1062. return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY));
  1063. }
  1064. for ($i = 0; $i < count($n->value); ++$i) {
  1065. if ( $n->value[$i] ) {
  1066. $temp = decbin($n->value[$i]);
  1067. $j = strlen($temp) - strrpos($temp, '1') - 1;
  1068. $j+= 26 * $i;
  1069. break;
  1070. }
  1071. }
  1072.  
  1073. $mod1 = $n->copy();
  1074. $mod1->_rshift($j);
  1075. $mod2 = new Math_BigInteger();
  1076. $mod2->value = array(1);
  1077. $mod2->_lshift($j);
  1078.  
  1079. $part1 = ( $mod1->value != array(1) ) ? $this->_slidingWindow($e, $mod1, MATH_BIGINTEGER_MONTGOMERY) : new Math_BigInteger();
  1080. $part2 = $this->_slidingWindow($e, $mod2, MATH_BIGINTEGER_POWEROF2);
  1081.  
  1082. $y1 = $mod2->modInverse($mod1);
  1083. $y2 = $mod1->modInverse($mod2);
  1084.  
  1085. $result = $part1->multiply($mod2);
  1086. $result = $result->multiply($y1);
  1087.  
  1088. $temp = $part2->multiply($mod1);
  1089. $temp = $temp->multiply($y2);
  1090.  
  1091. $result = $result->add($temp);
  1092. list(, $result) = $result->divide($n);
  1093.  
  1094. return $this->_normalize($result);
  1095. }
  1096.  
  1097. function powMod($e, $n) {
  1098. return $this->modPow($e, $n);
  1099. }
  1100.  
  1101. function _slidingWindow($e, $n, $mode) {
  1102. static $window_ranges = array(7, 25, 81, 241, 673, 1793);
  1103.  
  1104. $e_value = $e->value;
  1105. $e_length = count($e_value) - 1;
  1106. $e_bits = decbin($e_value[$e_length]);
  1107. for ($i = $e_length - 1; $i >= 0; --$i) {
  1108. $e_bits.= str_pad(decbin($e_value[$i]), MATH_BIGINTEGER_BASE, '0', STR_PAD_LEFT);
  1109. }
  1110.  
  1111. $e_length = strlen($e_bits);
  1112.  
  1113. for ($i = 0, $window_size = 1; $e_length > $window_ranges[$i] && $i < count($window_ranges); ++$window_size, ++$i);
  1114.  
  1115. $n_value = $n->value;
  1116.  
  1117. $powers = array();
  1118. $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode);
  1119. $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);
  1120.  
  1121. $temp = 1 << ($window_size - 1);
  1122. for ($i = 1; $i < $temp; ++$i) {
  1123. $i2 = $i << 1;
  1124. $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
  1125. }
  1126.  
  1127. $result = array(1);
  1128. $result = $this->_prepareReduce($result, $n_value, $mode);
  1129.  
  1130. for ($i = 0; $i < $e_length; ) {
  1131. if ( !$e_bits[$i] ) {
  1132. $result = $this->_squareReduce($result, $n_value, $mode);
  1133. ++$i;
  1134. } else {
  1135. for ($j = $window_size - 1; $j > 0; --$j) {
  1136. if ( !empty($e_bits[$i + $j]) ) {
  1137. break;
  1138. }
  1139. }
  1140.  
  1141. for ($k = 0; $k <= $j; ++$k) {// eg. the length of substr($e_bits, $i, $j+1)
  1142. $result = $this->_squareReduce($result, $n_value, $mode);
  1143. }
  1144.  
  1145. $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
  1146.  
  1147. $i+=$j + 1;
  1148. }
  1149. }
  1150.  
  1151. $temp = new Math_BigInteger();
  1152. $temp->value = $this->_reduce($result, $n_value, $mode);
  1153.  
  1154. return $temp;
  1155. }
  1156.  
  1157. function _reduce($x, $n, $mode) {
  1158. switch ($mode) {
  1159. case MATH_BIGINTEGER_MONTGOMERY:
  1160. return $this->_montgomery($x, $n);
  1161. case MATH_BIGINTEGER_BARRETT:
  1162. return $this->_barrett($x, $n);
  1163. case MATH_BIGINTEGER_POWEROF2:
  1164. $lhs = new Math_BigInteger();
  1165. $lhs->value = $x;
  1166. $rhs = new Math_BigInteger();
  1167. $rhs->value = $n;
  1168. return $x->_mod2($n);
  1169. case MATH_BIGINTEGER_CLASSIC:
  1170. $lhs = new Math_BigInteger();
  1171. $lhs->value = $x;
  1172. $rhs = new Math_BigInteger();
  1173. $rhs->value = $n;
  1174. list(, $temp) = $lhs->divide($rhs);
  1175. return $temp->value;
  1176. case MATH_BIGINTEGER_NONE:
  1177. return $x;
  1178. default:
  1179. // an invalid $mode was provided
  1180. }
  1181. }
  1182.  
  1183. function _prepareReduce($x, $n, $mode) {
  1184. if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
  1185. return $this->_prepMontgomery($x, $n);
  1186. }
  1187. return $this->_reduce($x, $n, $mode);
  1188. }
  1189.  
  1190. function _multiplyReduce($x, $y, $n, $mode) {
  1191. if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
  1192. return $this->_montgomeryMultiply($x, $y, $n);
  1193. }
  1194. $temp = $this->_multiply($x, false, $y, false);
  1195. return $this->_reduce($temp[MATH_BIGINTEGER_VALUE], $n, $mode);
  1196. }
  1197.  
  1198. function _squareReduce($x, $n, $mode) {
  1199. if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
  1200. return $this->_montgomeryMultiply($x, $x, $n);
  1201. }
  1202. return $this->_reduce($this->_square($x), $n, $mode);
  1203. }
  1204.  
  1205. function _mod2($n) {
  1206. $temp = new Math_BigInteger();
  1207. $temp->value = array(1);
  1208. return $this->bitwise_and($n->subtract($temp));
  1209. }
  1210.  
  1211. function _barrett($n, $m) {
  1212. static $cache = array(
  1213. MATH_BIGINTEGER_VARIABLE => array(),
  1214. MATH_BIGINTEGER_DATA => array()
  1215. );
  1216.  
  1217. $m_length = count($m);
  1218.  
  1219. if (count($n) > 2 * $m_length) {
  1220. $lhs = new Math_BigInteger();
  1221. $rhs = new Math_BigInteger();
  1222. $lhs->value = $n;
  1223. $rhs->value = $m;
  1224. list(, $temp) = $lhs->divide($rhs);
  1225. return $temp->value;
  1226. }
  1227.  
  1228. if ($m_length < 5) {
  1229. return $this->_regularBarrett($n, $m);
  1230. }
  1231.  
  1232. if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
  1233. $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
  1234. $cache[MATH_BIGINTEGER_VARIABLE][] = $m;
  1235.  
  1236. $lhs = new Math_BigInteger();
  1237. $lhs_value = &$lhs->value;
  1238. $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1));
  1239. $lhs_value[] = 1;
  1240. $rhs = new Math_BigInteger();
  1241. $rhs->value = $m;
  1242.  
  1243. list($u, $m1) = $lhs->divide($rhs);
  1244. $u = $u->value;
  1245. $m1 = $m1->value;
  1246.  
  1247. $cache[MATH_BIGINTEGER_DATA][] = array(
  1248. 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
  1249. 'm1'=> $m1 // m.length
  1250. );
  1251. } else {
  1252. extract($cache[MATH_BIGINTEGER_DATA][$key]);
  1253. }
  1254.  
  1255. $cutoff = $m_length + ($m_length >> 1);
  1256. $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
  1257. $msd = array_slice($n, $cutoff); // m.length >> 1
  1258. $lsd = $this->_trim($lsd);
  1259. $temp = $this->_multiply($msd, false, $m1, false);
  1260. $n = $this->_add($lsd, false, $temp[MATH_BIGINTEGER_VALUE], false); // m.length + (m.length >> 1) + 1
  1261.  
  1262. if ($m_length & 1) {
  1263. return $this->_regularBarrett($n[MATH_BIGINTEGER_VALUE], $m);
  1264. }
  1265.  
  1266. $temp = array_slice($n[MATH_BIGINTEGER_VALUE], $m_length - 1);
  1267. $temp = $this->_multiply($temp, false, $u, false);
  1268. $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], ($m_length >> 1) + 1);
  1269. $temp = $this->_multiply($temp, false, $m, false);
  1270.  
  1271. $result = $this->_subtract($n[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
  1272.  
  1273. while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false) >= 0) {
  1274. $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false);
  1275. }
  1276.  
  1277. return $result[MATH_BIGINTEGER_VALUE];
  1278. }
  1279.  
  1280. function _regularBarrett($x, $n) {
  1281. static $cache = array(
  1282. MATH_BIGINTEGER_VARIABLE => array(),
  1283. MATH_BIGINTEGER_DATA => array()
  1284. );
  1285.  
  1286. $n_length = count($n);
  1287.  
  1288. if (count($x) > 2 * $n_length) {
  1289. $lhs = new Math_BigInteger();
  1290. $rhs = new Math_BigInteger();
  1291. $lhs->value = $x;
  1292. $rhs->value = $n;
  1293. list(, $temp) = $lhs->divide($rhs);
  1294. return $temp->value;
  1295. }
  1296.  
  1297. if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
  1298. $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
  1299. $cache[MATH_BIGINTEGER_VARIABLE][] = $n;
  1300. $lhs = new Math_BigInteger();
  1301. $lhs_value = &$lhs->value;
  1302. $lhs_value = $this->_array_repeat(0, 2 * $n_length);
  1303. $lhs_value[] = 1;
  1304. $rhs = new Math_BigInteger();
  1305. $rhs->value = $n;
  1306. list($temp, ) = $lhs->divide($rhs);
  1307. $cache[MATH_BIGINTEGER_DATA][] = $temp->value;
  1308. }
  1309.  
  1310. $temp = array_slice($x, $n_length - 1);
  1311. $temp = $this->_multiply($temp, false, $cache[MATH_BIGINTEGER_DATA][$key], false);
  1312. $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], $n_length + 1);
  1313.  
  1314. $result = array_slice($x, 0, $n_length + 1);
  1315. $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1);
  1316.  
  1317. if ($this->_compare($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]) < 0) {
  1318. $corrector_value = $this->_array_repeat(0, $n_length + 1);
  1319. $corrector_value[] = 1;
  1320. $result = $this->_add($result, false, $corrector_value, false);
  1321. $result = $result[MATH_BIGINTEGER_VALUE];
  1322. }
  1323.  
  1324. $result = $this->_subtract($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]);
  1325. while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false) > 0) {
  1326. $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false);
  1327. }
  1328.  
  1329. return $result[MATH_BIGINTEGER_VALUE];
  1330. }
  1331.  
  1332. function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) {
  1333. $x_length = count($x_value);
  1334. $y_length = count($y_value);
  1335.  
  1336. if ( !$x_length || !$y_length ) { // a 0 is being multiplied
  1337. return array(
  1338. MATH_BIGINTEGER_VALUE => array(),
  1339. MATH_BIGINTEGER_SIGN => false
  1340. );
  1341. }
  1342.  
  1343. if ( $x_length < $y_length ) {
  1344. $temp = $x_value;
  1345. $x_value = $y_value;
  1346. $y_value = $temp;
  1347.  
  1348. $x_length = count($x_value);
  1349. $y_length = count($y_value);
  1350. }
  1351.  
  1352. $product_value = $this->_array_repeat(0, $x_length + $y_length);
  1353.  
  1354. $carry = 0;
  1355.  
  1356. for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
  1357. $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
  1358. $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL);
  1359. $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
  1360. }
  1361.  
  1362. if ($j < $stop) {
  1363. $product_value[$j] = $carry;
  1364. }
  1365.  
  1366. for ($i = 1; $i < $y_length; ++$i) {
  1367. $carry = 0;
  1368.  
  1369. for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
  1370. $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
  1371. $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL);
  1372. $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
  1373. }
  1374.  
  1375. if ($k < $stop) {
  1376. $product_value[$k] = $carry;
  1377. }
  1378. }
  1379.  
  1380. return array(
  1381. MATH_BIGINTEGER_VALUE => $this->_trim($product_value),
  1382. MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
  1383. );
  1384. }
  1385.  
  1386. function _montgomery($x, $n) {
  1387. static $cache = array(
  1388. MATH_BIGINTEGER_VARIABLE => array(),
  1389. MATH_BIGINTEGER_DATA => array()
  1390. );
  1391.  
  1392. if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
  1393. $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
  1394. $cache[MATH_BIGINTEGER_VARIABLE][] = $x;
  1395. $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($n);
  1396. }
  1397.  
  1398. $k = count($n);
  1399.  
  1400. $result = array(MATH_BIGINTEGER_VALUE => $x);
  1401.  
  1402. for ($i = 0; $i < $k; ++$i) {
  1403. $temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key];
  1404. $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL)));
  1405. $temp = $this->_regularMultiply(array($temp), $n);
  1406. $temp = array_merge($this->_array_repeat(0, $i), $temp);
  1407. $result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false);
  1408. }
  1409.  
  1410. $result[MATH_BIGINTEGER_VALUE] = array_slice($result[MATH_BIGINTEGER_VALUE], $k);
  1411.  
  1412. if ($this->_compare($result, false, $n, false) >= 0) {
  1413. $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], false, $n, false);
  1414. }
  1415.  
  1416. return $result[MATH_BIGINTEGER_VALUE];
  1417. }
  1418.  
  1419. function _montgomeryMultiply($x, $y, $m) {
  1420. $temp = $this->_multiply($x, false, $y, false);
  1421. return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m);
  1422.  
  1423. static $cache = array(
  1424. MATH_BIGINTEGER_VARIABLE => array(),
  1425. MATH_BIGINTEGER_DATA => array()
  1426. );
  1427.  
  1428. if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
  1429. $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
  1430. $cache[MATH_BIGINTEGER_VARIABLE][] = $m;
  1431. $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($m);
  1432. }
  1433.  
  1434. $n = max(count($x), count($y), count($m));
  1435. $x = array_pad($x, $n, 0);
  1436. $y = array_pad($y, $n, 0);
  1437. $m = array_pad($m, $n, 0);
  1438. $a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1));
  1439. for ($i = 0; $i < $n; ++$i) {
  1440. $temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0];
  1441. $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL)));
  1442. $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key];
  1443. $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL)));
  1444. $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
  1445. $a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
  1446. $a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1);
  1447. }
  1448. if ($this->_compare($a[MATH_BIGINTEGER_VALUE], false, $m, false) >= 0) {
  1449. $a = $this->_subtract($a[MATH_BIGINTEGER_VALUE], false, $m, false);
  1450. }
  1451. return $a[MATH_BIGINTEGER_VALUE];
  1452. }
  1453.  
  1454. function _prepMontgomery($x, $n) {
  1455. $lhs = new Math_BigInteger();
  1456. $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x);
  1457. $rhs = new Math_BigInteger();
  1458. $rhs->value = $n;
  1459.  
  1460. list(, $temp) = $lhs->divide($rhs);
  1461. return $temp->value;
  1462. }
  1463.  
  1464. function _modInverse67108864($x) // 2**26 == 67,108,864
  1465. {
  1466. $x = -$x[0];
  1467. $result = $x & 0x3; // x**-1 mod 2**2
  1468. $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
  1469. $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8
  1470. $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
  1471. $result = fmod($result * (2 - fmod($x * $result, MATH_BIGINTEGER_BASE_FULL)), MATH_BIGINTEGER_BASE_FULL); // x**-1 mod 2**26
  1472. return $result & MATH_BIGINTEGER_MAX_DIGIT;
  1473. }
  1474.  
  1475. function modInverse($n) {
  1476. switch ( MATH_BIGINTEGER_MODE ) {
  1477. case MATH_BIGINTEGER_MODE_GMP:
  1478. $temp = new Math_BigInteger();
  1479. $temp->value = gmp_invert($this->value, $n->value);
  1480.  
  1481. return ( $temp->value === false ) ? false : $this->_normalize($temp);
  1482. }
  1483.  
  1484. static $zero, $one;
  1485. if (!isset($zero)) {
  1486. $zero = new Math_BigInteger();
  1487. $one = new Math_BigInteger(1);
  1488. }
  1489.  
  1490. // $x mod -$n == $x mod $n.
  1491. $n = $n->abs();
  1492.  
  1493. if ($this->compare($zero) < 0) {
  1494. $temp = $this->abs();
  1495. $temp = $temp->modInverse($n);
  1496. return $this->_normalize($n->subtract($temp));
  1497. }
  1498.  
  1499. extract($this->extendedGCD($n));
  1500.  
  1501. if (!$gcd->equals($one)) {
  1502. return false;
  1503. }
  1504.  
  1505. $x = $x->compare($zero) < 0 ? $x->add($n) : $x;
  1506.  
  1507. return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
  1508. }
  1509.  
  1510. function extendedGCD($n) {
  1511. switch ( MATH_BIGINTEGER_MODE ) {
  1512. case MATH_BIGINTEGER_MODE_GMP:
  1513. extract(gmp_gcdext($this->value, $n->value));
  1514.  
  1515. return array(
  1516. 'gcd' => $this->_normalize(new Math_BigInteger($g)),
  1517. 'x' => $this->_normalize(new Math_BigInteger($s)),
  1518. 'y' => $this->_normalize(new Math_BigInteger($t))
  1519. );
  1520. case MATH_BIGINTEGER_MODE_BCMATH:
  1521. $u = $this->value;
  1522. $v = $n->value;
  1523.  
  1524. $a = '1';
  1525. $b = '0';
  1526. $c = '0';
  1527. $d = '1';
  1528.  
  1529. while (bccomp($v, '0', 0) != 0) {
  1530. $q = bcdiv($u, $v, 0);
  1531.  
  1532. $temp = $u;
  1533. $u = $v;
  1534. $v = bcsub($temp, bcmul($v, $q, 0), 0);
  1535.  
  1536. $temp = $a;
  1537. $a = $c;
  1538. $c = bcsub($temp, bcmul($a, $q, 0), 0);
  1539.  
  1540. $temp = $b;
  1541. $b = $d;
  1542. $d = bcsub($temp, bcmul($b, $q, 0), 0);
  1543. }
  1544.  
  1545. return array(
  1546. 'gcd' => $this->_normalize(new Math_BigInteger($u)),
  1547. 'x' => $this->_normalize(new Math_BigInteger($a)),
  1548. 'y' => $this->_normalize(new Math_BigInteger($b))
  1549. );
  1550. }
  1551.  
  1552. $y = $n->copy();
  1553. $x = $this->copy();
  1554. $g = new Math_BigInteger();
  1555. $g->value = array(1);
  1556.  
  1557. while ( !(($x->value[0] & 1)|| ($y->value[0] & 1)) ) {
  1558. $x->_rshift(1);
  1559. $y->_rshift(1);
  1560. $g->_lshift(1);
  1561. }
  1562.  
  1563. $u = $x->copy();
  1564. $v = $y->copy();
  1565.  
  1566. $a = new Math_BigInteger();
  1567. $b = new Math_BigInteger();
  1568. $c = new Math_BigInteger();
  1569. $d = new Math_BigInteger();
  1570.  
  1571. $a->value = $d->value = $g->value = array(1);
  1572. $b->value = $c->value = array();
  1573.  
  1574. while ( !empty($u->value) ) {
  1575. while ( !($u->value[0] & 1) ) {
  1576. $u->_rshift(1);
  1577. if ( (!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1)) ) {
  1578. $a = $a->add($y);
  1579. $b = $b->subtract($x);
  1580. }
  1581. $a->_rshift(1);
  1582. $b->_rshift(1);
  1583. }
  1584.  
  1585. while ( !($v->value[0] & 1) ) {
  1586. $v->_rshift(1);
  1587. if ( (!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1)) ) {
  1588. $c = $c->add($y);
  1589. $d = $d->subtract($x);
  1590. }
  1591. $c->_rshift(1);
  1592. $d->_rshift(1);
  1593. }
  1594.  
  1595. if ($u->compare($v) >= 0) {
  1596. $u = $u->subtract($v);
  1597. $a = $a->subtract($c);
  1598. $b = $b->subtract($d);
  1599. } else {
  1600. $v = $v->subtract($u);
  1601. $c = $c->subtract($a);
  1602. $d = $d->subtract($b);
  1603. }
  1604. }
  1605.  
  1606. return array(
  1607. 'gcd' => $this->_normalize($g->multiply($v)),
  1608. 'x' => $this->_normalize($c),
  1609. 'y' => $this->_normalize($d)
  1610. );
  1611. }
  1612.  
  1613. function gcd($n) {
  1614. extract($this->extendedGCD($n));
  1615. return $gcd;
  1616. }
  1617.  
  1618. function abs() {
  1619. $temp = new Math_BigInteger();
  1620.  
  1621. switch ( MATH_BIGINTEGER_MODE ) {
  1622. case MATH_BIGINTEGER_MODE_GMP:
  1623. $temp->value = gmp_abs($this->value);
  1624. break;
  1625. case MATH_BIGINTEGER_MODE_BCMATH:
  1626. $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
  1627. break;
  1628. default:
  1629. $temp->value = $this->value;
  1630. }
  1631.  
  1632. return $temp;
  1633. }
  1634.  
  1635. function compare($y) {
  1636. switch ( MATH_BIGINTEGER_MODE ) {
  1637. case MATH_BIGINTEGER_MODE_GMP:
  1638. return gmp_cmp($this->value, $y->value);
  1639. case MATH_BIGINTEGER_MODE_BCMATH:
  1640. return bccomp($this->value, $y->value, 0);
  1641. }
  1642.  
  1643. return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
  1644. }
  1645.  
  1646. function _compare($x_value, $x_negative, $y_value, $y_negative) {
  1647. if ( $x_negative != $y_negative ) {
  1648. return ( !$x_negative && $y_negative ) ? 1 : -1;
  1649. }
  1650.  
  1651. $result = $x_negative ? -1 : 1;
  1652.  
  1653. if ( count($x_value) != count($y_value) ) {
  1654. return ( count($x_value) > count($y_value) ) ? $result : -$result;
  1655. }
  1656. $size = max(count($x_value), count($y_value));
  1657.  
  1658. $x_value = array_pad($x_value, $size, 0);
  1659. $y_value = array_pad($y_value, $size, 0);
  1660.  
  1661. for ($i = count($x_value) - 1; $i >= 0; --$i) {
  1662. if ($x_value[$i] != $y_value[$i]) {
  1663. return ( $x_value[$i] > $y_value[$i] ) ? $result : -$result;
  1664. }
  1665. }
  1666.  
  1667. return 0;
  1668. }
  1669.  
  1670. function equals($x) {
  1671. switch ( MATH_BIGINTEGER_MODE ) {
  1672. case MATH_BIGINTEGER_MODE_GMP:
  1673. return gmp_cmp($this->value, $x->value) == 0;
  1674. default:
  1675. return $this->value === $x->value && $this->is_negative == $x->is_negative;
  1676. }
  1677. }
  1678.  
  1679. function setPrecision($bits) {
  1680. $this->precision = $bits;
  1681. if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ) {
  1682. $this->bitmask = new Math_BigInteger(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
  1683. } else {
  1684. $this->bitmask = new Math_BigInteger(bcpow('2', $bits, 0));
  1685. }
  1686.  
  1687. $temp = $this->_normalize($this);
  1688. $this->value = $temp->value;
  1689. }
  1690.  
  1691. function bitwise_rightShift($shift) {
  1692. $temp = new Math_BigInteger();
  1693.  
  1694. switch ( MATH_BIGINTEGER_MODE ) {
  1695. case MATH_BIGINTEGER_MODE_GMP:
  1696. static $two;
  1697.  
  1698. if (!isset($two)) {
  1699. $two = gmp_init('2');
  1700. }
  1701.  
  1702. $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));
  1703.  
  1704. break;
  1705. case MATH_BIGINTEGER_MODE_BCMATH:
  1706. $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
  1707.  
  1708. break;
  1709. default:
  1710. $temp->value = $this->value;
  1711. $temp->_rshift($shift);
  1712. }
  1713.  
  1714. return $this->_normalize($temp);
  1715. }
  1716.  
  1717. function bitwise_leftShift($shift) {
  1718. $temp = new Math_BigInteger();
  1719.  
  1720. switch ( MATH_BIGINTEGER_MODE ) {
  1721. case MATH_BIGINTEGER_MODE_GMP:
  1722. static $two;
  1723.  
  1724. if (!isset($two)) {
  1725. $two = gmp_init('2');
  1726. }
  1727.  
  1728. $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
  1729.  
  1730. break;
  1731. case MATH_BIGINTEGER_MODE_BCMATH:
  1732. $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
  1733.  
  1734. break;
  1735. default:
  1736. $temp->value = $this->value;
  1737. $temp->_lshift($shift);
  1738. }
  1739.  
  1740. return $this->_normalize($temp);
  1741. }
  1742.  
  1743. function bitwise_leftRotate($shift) {
  1744. $bits = $this->toBytes();
  1745.  
  1746. if ($this->precision > 0) {
  1747. $precision = $this->precision;
  1748. if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
  1749. $mask = $this->bitmask->subtract(new Math_BigInteger(1));
  1750. $mask = $mask->toBytes();
  1751. } else {
  1752. $mask = $this->bitmask->toBytes();
  1753. }
  1754. } else {
  1755. $temp = ord($bits[0]);
  1756. for ($i = 0; $temp >> $i; ++$i);
  1757. $precision = 8 * strlen($bits) - 8 + $i;
  1758. $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
  1759. }
  1760.  
  1761. if ($shift < 0) {
  1762. $shift+= $precision;
  1763. }
  1764. $shift%= $precision;
  1765.  
  1766. if (!$shift) {
  1767. return $this->copy();
  1768. }
  1769.  
  1770. $left = $this->bitwise_leftShift($shift);
  1771. $left = $left->bitwise_and(new Math_BigInteger($mask, 256));
  1772. $right = $this->bitwise_rightShift($precision - $shift);
  1773. $result = MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
  1774. return $this->_normalize($result);
  1775. }
  1776.  
  1777. function bitwise_rightRotate($shift) {
  1778. return $this->bitwise_leftRotate(-$shift);
  1779. }
  1780.  
  1781. function _make_odd() {
  1782. switch ( MATH_BIGINTEGER_MODE ) {
  1783. case MATH_BIGINTEGER_MODE_GMP:
  1784. gmp_setbit($this->value, 0);
  1785. break;
  1786. case MATH_BIGINTEGER_MODE_BCMATH:
  1787. if ($this->value[strlen($this->value) - 1] % 2 == 0) {
  1788. $this->value = bcadd($this->value, '1');
  1789. }
  1790. break;
  1791. default:
  1792. $this->value[0] |= 1;
  1793. }
  1794. }
  1795.  
  1796. function _lshift($shift) {
  1797. if ( $shift == 0 ) {
  1798. return;
  1799. }
  1800.  
  1801. $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE);
  1802. $shift %= MATH_BIGINTEGER_BASE;
  1803. $shift = 1 << $shift;
  1804.  
  1805. $carry = 0;
  1806.  
  1807. for ($i = 0; $i < count($this->value); ++$i) {
  1808. $temp = $this->value[$i] * $shift + $carry;
  1809. $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL);
  1810. $this->value[$i] = (int) ($temp - $carry * MATH_BIGINTEGER_BASE_FULL);
  1811. }
  1812.  
  1813. if ( $carry ) {
  1814. $this->value[] = $carry;
  1815. }
  1816.  
  1817. while ($num_digits--) {
  1818. array_unshift($this->value, 0);
  1819. }
  1820. }
  1821.  
  1822. function _rshift($shift) {
  1823. if ($shift == 0) {
  1824. return;
  1825. }
  1826.  
  1827. $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE);
  1828. $shift %= MATH_BIGINTEGER_BASE;
  1829. $carry_shift = MATH_BIGINTEGER_BASE - $shift;
  1830. $carry_mask = (1 << $shift) - 1;
  1831.  
  1832. if ( $num_digits ) {
  1833. $this->value = array_slice($this->value, $num_digits);
  1834. }
  1835.  
  1836. $carry = 0;
  1837.  
  1838. for ($i = count($this->value) - 1; $i >= 0; --$i) {
  1839. $temp = $this->value[$i] >> $shift | $carry;
  1840. $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
  1841. $this->value[$i] = $temp;
  1842. }
  1843.  
  1844. $this->value = $this->_trim($this->value);
  1845. }
  1846.  
  1847. function _normalize($result) {
  1848. $result->precision = $this->precision;
  1849. $result->bitmask = $this->bitmask;
  1850.  
  1851. switch ( MATH_BIGINTEGER_MODE ) {
  1852. case MATH_BIGINTEGER_MODE_GMP:
  1853. if (!empty($result->bitmask->value)) {
  1854. $result->value = gmp_and($result->value, $result->bitmask->value);
  1855. }
  1856.  
  1857. return $result;
  1858. case MATH_BIGINTEGER_MODE_BCMATH:
  1859. if (!empty($result->bitmask->value)) {
  1860. $result->value = bcmod($result->value, $result->bitmask->value);
  1861. }
  1862.  
  1863. return $result;
  1864. }
  1865.  
  1866. $value = &$result->value;
  1867.  
  1868. if ( !count($value) ) {
  1869. return $result;
  1870. }
  1871.  
  1872. $value = $this->_trim($value);
  1873.  
  1874. if (!empty($result->bitmask->value)) {
  1875. $length = min(count($value), count($this->bitmask->value));
  1876. $value = array_slice($value, 0, $length);
  1877.  
  1878. for ($i = 0; $i < $length; ++$i) {
  1879. $value[$i] = $value[$i] & $this->bitmask->value[$i];
  1880. }
  1881. }
  1882.  
  1883. return $result;
  1884. }
  1885.  
  1886. function _trim($value) {
  1887. for ($i = count($value) - 1; $i >= 0; --$i) {
  1888. if ( $value[$i] ) {
  1889. break;
  1890. }
  1891. unset($value[$i]);
  1892. }
  1893.  
  1894. return $value;
  1895. }
  1896.  
  1897. function _array_repeat($input, $multiplier) {
  1898. return ($multiplier) ? array_fill(0, $multiplier, $input) : array();
  1899. }
  1900.  
  1901. function _base256_lshift(&$x, $shift) {
  1902. if ($shift == 0) {
  1903. return;
  1904. }
  1905.  
  1906. $num_bytes = $shift >> 3; // eg. floor($shift/8)
  1907. $shift &= 7; // eg. $shift % 8
  1908.  
  1909. $carry = 0;
  1910. for ($i = strlen($x) - 1; $i >= 0; --$i) {
  1911. $temp = ord($x[$i]) << $shift | $carry;
  1912. $x[$i] = chr($temp);
  1913. $carry = $temp >> 8;
  1914. }
  1915. $carry = ($carry != 0) ? chr($carry) : '';
  1916. $x = $carry . $x . str_repeat(chr(0), $num_bytes);
  1917. }
  1918.  
  1919. function _base256_rshift(&$x, $shift) {
  1920. if ($shift == 0) {
  1921. $x = ltrim($x, chr(0));
  1922. return '';
  1923. }
  1924.  
  1925. $num_bytes = $shift >> 3; // eg. floor($shift/8)
  1926. $shift &= 7; // eg. $shift % 8
  1927.  
  1928. $remainder = '';
  1929. if ($num_bytes) {
  1930. $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
  1931. $remainder = substr($x, $start);
  1932. $x = substr($x, 0, -$num_bytes);
  1933. }
  1934.  
  1935. $carry = 0;
  1936. $carry_shift = 8 - $shift;
  1937. for ($i = 0; $i < strlen($x); ++$i) {
  1938. $temp = (ord($x[$i]) >> $shift) | $carry;
  1939. $carry = (ord($x[$i]) << $carry_shift) & 0xFF;
  1940. $x[$i] = chr($temp);
  1941. }
  1942. $x = ltrim($x, chr(0));
  1943.  
  1944. $remainder = chr($carry >> $carry_shift) . $remainder;
  1945.  
  1946. return ltrim($remainder, chr(0));
  1947. }
  1948.  
  1949. function _int2bytes($x) {
  1950. return ltrim(pack('N', $x), chr(0));
  1951. }
  1952.  
  1953. function _bytes2int($x) {
  1954. $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
  1955. return $temp['int'];
  1956. }
  1957.  
  1958. function _encodeASN1Length($length) {
  1959. if ($length <= 0x7F) {
  1960. return chr($length);
  1961. }
  1962.  
  1963. $temp = ltrim(pack('N', $length), chr(0));
  1964. return pack('Ca*', 0x80 | strlen($temp), $temp);
  1965. }
  1966. }
  1967.  
  1968. class BaseN {
  1969.  
  1970. private $base;
  1971. private $radix;
  1972. private $bi256, $one, $zero;
  1973.  
  1974. function __construct($base) {
  1975. $this->base = $base;
  1976. $this->radix = new Math_BigInteger(strlen($base));
  1977. $this->bi256 = new Math_BigInteger(256);
  1978. $this->zero = new Math_BigInteger(0);
  1979. $this->one = new Math_BigInteger(1);
  1980. }
  1981.  
  1982. public function encode($text) {
  1983. $big = $this->one;
  1984. for ($j = 0; $j < strlen($text); $j++) {
  1985. $big = $big->multiply($this->bi256)->add(new Math_BigInteger(ord($text[$j])));
  1986. }
  1987. $result = "";
  1988. while (!$this->zero->equals($big)) {
  1989. $parts = $big->divide($this->radix);
  1990. $small = intval($parts[1]->toString());
  1991. $big = $parts[0];
  1992. $result = $this->base[$small] . $result;
  1993. }
  1994. return $result;
  1995. }
  1996.  
  1997. public function decode($text) {
  1998. $big = $this->zero;
  1999. for ($j = 0; $j < strlen($text); $j++) {
  2000. $i = strpos($this->base, $text[$j]);
  2001. $big = $big->multiply($this->radix)->add(new Math_BigInteger($i));
  2002. }
  2003. $result = "";
  2004. while (!$this->zero->equals($big)) {
  2005. $parts = $big->divide($this->bi256);
  2006. $small = $parts[1]->toBytes();
  2007. $big = $parts[0];
  2008. $result = $small . $result;
  2009. }
  2010. return substr($result, 1);
  2011. }
  2012. }
  2013.  
  2014. // Passa o alfabeto como parâmetro. Tem 62 caracteres aqui, então são 62 símbolos.
  2015. $k = new BaseN("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
  2016. $x = "The quick brown fox jumps over a lazy dog";
  2017. echo $x . "\n";
  2018. $c = $k->encode($x);
  2019. echo $c . "\n"; // Escreve "1u9WLfG65OMtVkQWPtWDcC6o8IjI5td5l9DzpilIK4Nyx81tKLRrStPj"
  2020. $d = $k->decode($c);
  2021. echo $d . "\n"; // Escreve "The quick brown fox jumps over a lazy dog"
  2022. ?>
Success #stdin #stdout 0.03s 24400KB
stdin
Standard input is empty
stdout
The quick brown fox jumps over a lazy dog
1u9WLfG65OMtVkQWPtWDcC6o8IjI5td5l9DzpilIK4Nyx81tKLRrStPj
The quick brown fox jumps over a lazy dog