1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* The MIT License (MIT) |
5
|
|
|
* |
6
|
|
|
* Copyright (c) 2014-2016 Spomky-Labs |
7
|
|
|
* |
8
|
|
|
* This software may be modified and distributed under the terms |
9
|
|
|
* of the MIT license. See the LICENSE file for details. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Jose\Util; |
13
|
|
|
|
14
|
|
|
class BigInteger |
15
|
|
|
{ |
16
|
|
|
const MONTGOMERY = 0; |
17
|
|
|
|
18
|
|
|
const BARRETT = 1; |
19
|
|
|
|
20
|
|
|
const POWEROF2 = 2; |
21
|
|
|
|
22
|
|
|
const CLASSIC = 3; |
23
|
|
|
|
24
|
|
|
const NONE = 4; |
25
|
|
|
|
26
|
|
|
/**#@+ |
27
|
|
|
* Array constants |
28
|
|
|
* |
29
|
|
|
* Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and |
30
|
|
|
* multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. |
31
|
|
|
* |
32
|
|
|
*/ |
33
|
|
|
/** |
34
|
|
|
* $result[self::VALUE] contains the value. |
35
|
|
|
*/ |
36
|
|
|
const VALUE = 0; |
37
|
|
|
/** |
38
|
|
|
* $result[self::SIGN] contains the sign. |
39
|
|
|
*/ |
40
|
|
|
const SIGN = 1; |
41
|
|
|
/**#@-*/ |
42
|
|
|
|
43
|
|
|
/**#@+ |
44
|
|
|
*/ |
45
|
|
|
/** |
46
|
|
|
* Cache constants. |
47
|
|
|
* |
48
|
|
|
* $cache[self::VARIABLE] tells us whether or not the cached data is still valid. |
49
|
|
|
*/ |
50
|
|
|
const VARIABLE = 0; |
51
|
|
|
/** |
52
|
|
|
* $cache[self::DATA] contains the cached data. |
53
|
|
|
*/ |
54
|
|
|
const DATA = 1; |
55
|
|
|
/**#@-*/ |
56
|
|
|
|
57
|
|
|
/**#@+ |
58
|
|
|
* Mode constants. |
59
|
|
|
* |
60
|
|
|
*/ |
61
|
|
|
/** |
62
|
|
|
* To use the pure-PHP implementation. |
63
|
|
|
*/ |
64
|
|
|
const MODE_INTERNAL = 1; |
65
|
|
|
/** |
66
|
|
|
* To use the BCMath library. |
67
|
|
|
* |
68
|
|
|
* (if enabled; otherwise, the internal implementation will be used) |
69
|
|
|
*/ |
70
|
|
|
const MODE_BCMATH = 2; |
71
|
|
|
/** |
72
|
|
|
* To use the GMP library. |
73
|
|
|
* |
74
|
|
|
* (if present; otherwise, either the BCMath or the internal implementation will be used) |
75
|
|
|
*/ |
76
|
|
|
const MODE_GMP = 3; |
77
|
|
|
/**#@-*/ |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Karatsuba Cutoff. |
81
|
|
|
* |
82
|
|
|
* At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? |
83
|
|
|
*/ |
84
|
|
|
const KARATSUBA_CUTOFF = 25; |
85
|
|
|
|
86
|
|
|
/**#@+ |
87
|
|
|
* Static properties used by the pure-PHP implementation. |
88
|
|
|
* |
89
|
|
|
* @see __construct() |
90
|
|
|
*/ |
91
|
|
|
protected static $base; |
92
|
|
|
protected static $baseFull; |
93
|
|
|
protected static $maxDigit; |
94
|
|
|
protected static $msb; |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* $max10 in greatest $max10Len satisfying |
98
|
|
|
* $max10 = 10**$max10Len <= 2**$base. |
99
|
|
|
*/ |
100
|
|
|
protected static $max10; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* $max10Len in greatest $max10Len satisfying |
104
|
|
|
* $max10 = 10**$max10Len <= 2**$base. |
105
|
|
|
*/ |
106
|
|
|
protected static $max10Len; |
107
|
|
|
protected static $maxDigit2; |
108
|
|
|
/**#@-*/ |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Holds the BigInteger's value. |
112
|
|
|
* |
113
|
|
|
* @var array |
114
|
|
|
*/ |
115
|
|
|
private $value; |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Holds the BigInteger's magnitude. |
119
|
|
|
* |
120
|
|
|
* @var bool |
121
|
|
|
*/ |
122
|
|
|
private $is_negative = false; |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Precision. |
126
|
|
|
*/ |
127
|
|
|
private $precision = -1; |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Precision Bitmask. |
131
|
|
|
*/ |
132
|
|
|
private $bitmask = false; |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Mode independent value used for serialization. |
136
|
|
|
* |
137
|
|
|
* If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for |
138
|
|
|
* a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value, |
139
|
|
|
* however, $this->hex is only calculated when $this->__sleep() is called. |
140
|
|
|
* |
141
|
|
|
* @var string |
142
|
|
|
*/ |
143
|
|
|
private $hex; |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers. |
147
|
|
|
* |
148
|
|
|
* If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using |
149
|
|
|
* two's compliment. The sole exception to this is -10, which is treated the same as 10 is. |
150
|
|
|
* |
151
|
|
|
* Here's an example: |
152
|
|
|
* <code> |
153
|
|
|
* <?php |
154
|
|
|
* $a = new \Jose\Util\in base-16 |
155
|
|
|
* |
156
|
|
|
* echo $a->toString(); // outputs 50 |
157
|
|
|
* ?> |
158
|
|
|
* </code> |
159
|
|
|
* |
160
|
|
|
* @param $x base-10 number or base-$base number if $base set. |
161
|
|
|
* @param int $base |
162
|
|
|
* |
163
|
|
|
* @return \Jose\Util\BigInteger |
|
|
|
|
164
|
|
|
*/ |
165
|
|
|
public function __construct($x = 0, $base = 10) |
166
|
|
|
{ |
167
|
|
|
if (!defined('MATH_BIGINTEGER_MODE')) { |
168
|
|
|
switch (true) { |
169
|
|
|
case extension_loaded('gmp'): |
170
|
|
|
define('MATH_BIGINTEGER_MODE', self::MODE_GMP); |
171
|
|
|
break; |
172
|
|
|
case extension_loaded('bcmath'): |
173
|
|
|
define('MATH_BIGINTEGER_MODE', self::MODE_BCMATH); |
174
|
|
|
break; |
175
|
|
|
default: |
176
|
|
|
define('MATH_BIGINTEGER_MODE', self::MODE_INTERNAL); |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { |
181
|
|
|
define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
if (!defined('PHP_INT_SIZE')) { |
185
|
|
|
define('PHP_INT_SIZE', 4); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) { |
189
|
|
|
switch (PHP_INT_SIZE) { |
190
|
|
|
case 8: // use 64-bit integers if int size is 8 bytes |
191
|
|
|
self::$base = 31; |
192
|
|
|
self::$baseFull = 0x80000000; |
193
|
|
|
self::$maxDigit = 0x7FFFFFFF; |
194
|
|
|
self::$msb = 0x40000000; |
195
|
|
|
self::$max10 = 1000000000; |
196
|
|
|
self::$max10Len = 9; |
197
|
|
|
self::$maxDigit2 = pow(2, 62); |
198
|
|
|
break; |
199
|
|
|
//case 4: // use 64-bit floats if int size is 4 bytes |
200
|
|
|
default: |
201
|
|
|
self::$base = 26; |
202
|
|
|
self::$baseFull = 0x4000000; |
203
|
|
|
self::$maxDigit = 0x3FFFFFF; |
204
|
|
|
self::$msb = 0x2000000; |
205
|
|
|
self::$max10 = 10000000; |
206
|
|
|
self::$max10Len = 7; |
207
|
|
|
self::$maxDigit2 = pow(2, 52); // pow() prevents truncation |
208
|
|
|
} |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
212
|
|
|
case self::MODE_GMP: |
213
|
|
|
switch (true) { |
214
|
|
|
case is_resource($x) && get_resource_type($x) == 'GMP integer': |
215
|
|
|
// PHP 5.6 switched GMP from using resources to objects |
216
|
|
|
case $x instanceof \GMP: |
|
|
|
|
217
|
|
|
$this->value = $x; |
|
|
|
|
218
|
|
|
|
219
|
|
|
return; |
220
|
|
|
} |
221
|
|
|
$this->value = gmp_init(0); |
|
|
|
|
222
|
|
|
break; |
223
|
|
|
case self::MODE_BCMATH: |
224
|
|
|
$this->value = '0'; |
|
|
|
|
225
|
|
|
break; |
226
|
|
|
default: |
227
|
|
|
$this->value = []; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
// '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48 |
231
|
|
|
// '0' is the only value like this per http://php.net/empty |
232
|
|
|
if (empty($x) && (abs($base) != 256 || $x !== '0')) { |
|
|
|
|
233
|
|
|
return; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
switch ($base) { |
237
|
|
|
case -256: |
238
|
|
|
if (ord($x[0]) & 0x80) { |
239
|
|
|
$x = ~$x; |
240
|
|
|
$this->is_negative = true; |
241
|
|
|
} |
242
|
|
|
case 256: |
243
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
244
|
|
|
case self::MODE_GMP: |
245
|
|
|
$sign = $this->is_negative ? '-' : ''; |
246
|
|
|
$this->value = gmp_init($sign.'0x'.bin2hex($x)); |
|
|
|
|
247
|
|
|
break; |
248
|
|
|
case self::MODE_BCMATH: |
249
|
|
|
// round $len to the nearest 4 (thanks, DavidMJ!) |
250
|
|
|
$len = (strlen($x) + 3) & 0xFFFFFFFC; |
251
|
|
|
|
252
|
|
|
$x = str_pad($x, $len, chr(0), STR_PAD_LEFT); |
253
|
|
|
|
254
|
|
|
for ($i = 0; $i < $len; $i += 4) { |
255
|
|
|
$this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32 |
|
|
|
|
256
|
|
|
$this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0); |
|
|
|
|
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
if ($this->is_negative) { |
260
|
|
|
$this->value = '-'.$this->value; |
|
|
|
|
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
break; |
264
|
|
|
// converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb) |
265
|
|
|
default: |
266
|
|
|
while (strlen($x)) { |
267
|
|
|
$this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base)); |
268
|
|
|
} |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
if ($this->is_negative) { |
272
|
|
|
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { |
273
|
|
|
$this->is_negative = false; |
274
|
|
|
} |
275
|
|
|
$temp = $this->add(new static('-1')); |
276
|
|
|
$this->value = $temp->value; |
277
|
|
|
} |
278
|
|
|
break; |
279
|
|
|
case 16: |
280
|
|
|
case -16: |
281
|
|
|
if ($base > 0 && $x[0] == '-') { |
282
|
|
|
$this->is_negative = true; |
283
|
|
|
$x = substr($x, 1); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
$x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x); |
287
|
|
|
|
288
|
|
|
$is_negative = false; |
289
|
|
|
if ($base < 0 && hexdec($x[0]) >= 8) { |
290
|
|
|
$this->is_negative = $is_negative = true; |
291
|
|
|
$x = bin2hex(~hex2bin($x)); |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
295
|
|
|
case self::MODE_GMP: |
296
|
|
|
$temp = $this->is_negative ? '-0x'.$x : '0x'.$x; |
297
|
|
|
$this->value = gmp_init($temp); |
|
|
|
|
298
|
|
|
$this->is_negative = false; |
299
|
|
|
break; |
300
|
|
|
case self::MODE_BCMATH: |
301
|
|
|
$x = (strlen($x) & 1) ? '0'.$x : $x; |
302
|
|
|
$temp = new static(hex2bin($x), 256); |
303
|
|
|
$this->value = $this->is_negative ? '-'.$temp->value : $temp->value; |
|
|
|
|
304
|
|
|
$this->is_negative = false; |
305
|
|
|
break; |
306
|
|
|
default: |
307
|
|
|
$x = (strlen($x) & 1) ? '0'.$x : $x; |
308
|
|
|
$temp = new static(hex2bin($x), 256); |
309
|
|
|
$this->value = $temp->value; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
if ($is_negative) { |
313
|
|
|
$temp = $this->add(new static('-1')); |
314
|
|
|
$this->value = $temp->value; |
315
|
|
|
} |
316
|
|
|
break; |
317
|
|
|
case 10: |
318
|
|
|
case -10: |
319
|
|
|
// (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that |
320
|
|
|
// (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals) |
321
|
|
|
// [^-0-9].*: find any non-numeric characters and then any characters that follow that |
322
|
|
|
$x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x); |
323
|
|
|
|
324
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
325
|
|
|
case self::MODE_GMP: |
326
|
|
|
$this->value = gmp_init($x); |
|
|
|
|
327
|
|
|
break; |
328
|
|
|
case self::MODE_BCMATH: |
329
|
|
|
// explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different |
330
|
|
|
// results then doing it on '-1' does (modInverse does $x[0]) |
331
|
|
|
$this->value = $x === '-' ? '0' : (string) $x; |
|
|
|
|
332
|
|
|
break; |
333
|
|
|
default: |
334
|
|
|
$temp = new static(); |
335
|
|
|
|
336
|
|
|
$multiplier = new static(); |
337
|
|
|
$multiplier->value = [self::$max10]; |
338
|
|
|
|
339
|
|
|
if ($x[0] == '-') { |
340
|
|
|
$this->is_negative = true; |
341
|
|
|
$x = substr($x, 1); |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
$x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT); |
345
|
|
|
while (strlen($x)) { |
346
|
|
|
$temp = $temp->multiply($multiplier); |
347
|
|
|
$temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256)); |
348
|
|
|
$x = substr($x, self::$max10Len); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
$this->value = $temp->value; |
352
|
|
|
} |
353
|
|
|
break; |
354
|
|
|
case 2: // base-2 support originally implemented by Lluis Pamies - thanks! |
355
|
|
|
case -2: |
356
|
|
|
if ($base > 0 && $x[0] == '-') { |
357
|
|
|
$this->is_negative = true; |
358
|
|
|
$x = substr($x, 1); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
$x = preg_replace('#^([01]*).*#', '$1', $x); |
362
|
|
|
$x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT); |
363
|
|
|
|
364
|
|
|
$str = '0x'; |
365
|
|
|
while (strlen($x)) { |
366
|
|
|
$part = substr($x, 0, 4); |
367
|
|
|
$str .= dechex(bindec($part)); |
368
|
|
|
$x = substr($x, 4); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
if ($this->is_negative) { |
372
|
|
|
$str = '-'.$str; |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
$temp = new static($str, 8 * $base); // ie. either -16 or +16 |
376
|
|
|
$this->value = $temp->value; |
377
|
|
|
$this->is_negative = $temp->is_negative; |
378
|
|
|
|
379
|
|
|
break; |
380
|
|
|
default: |
381
|
|
|
// base not supported, so we'll let $this == 0 |
382
|
|
|
} |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* Converts a BigInteger to a byte string (eg. base-256). |
387
|
|
|
* |
388
|
|
|
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're |
389
|
|
|
* saved as two's compliment. |
390
|
|
|
* |
391
|
|
|
* Here's an example: |
392
|
|
|
* <code> |
393
|
|
|
* <?php |
394
|
|
|
* $a = new \Jose\Util\ger('65'); |
395
|
|
|
* |
396
|
|
|
* echo $a->toBytes(); // outputs chr(65) |
397
|
|
|
* ?> |
398
|
|
|
* </code> |
399
|
|
|
* |
400
|
|
|
* @param bool $twos_compliment |
401
|
|
|
* |
402
|
|
|
* @return string |
403
|
|
|
* |
404
|
|
|
* @internal Converts a base-2**26 number to base-2**8 |
405
|
|
|
*/ |
406
|
|
|
public function toBytes($twos_compliment = false) |
407
|
|
|
{ |
408
|
|
|
if ($twos_compliment) { |
409
|
|
|
$comparison = $this->compare(new static()); |
410
|
|
|
if ($comparison == 0) { |
411
|
|
|
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
$temp = $comparison < 0 ? $this->add(new static(1)) : $this; |
415
|
|
|
$bytes = $temp->toBytes(); |
416
|
|
|
|
417
|
|
|
if (empty($bytes)) { // eg. if the number we're trying to convert is -1 |
418
|
|
|
$bytes = chr(0); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
if (ord($bytes[0]) & 0x80) { |
422
|
|
|
$bytes = chr(0).$bytes; |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
return $comparison < 0 ? ~$bytes : $bytes; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
429
|
|
|
case self::MODE_GMP: |
430
|
|
|
if (gmp_cmp($this->value, gmp_init(0)) == 0) { |
431
|
|
|
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
$temp = gmp_strval(gmp_abs($this->value), 16); |
435
|
|
|
$temp = (strlen($temp) & 1) ? '0'.$temp : $temp; |
436
|
|
|
$temp = hex2bin($temp); |
437
|
|
|
|
438
|
|
|
return $this->precision > 0 ? |
439
|
|
|
substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : |
440
|
|
|
ltrim($temp, chr(0)); |
441
|
|
|
case self::MODE_BCMATH: |
442
|
|
|
if ($this->value === '0') { |
443
|
|
|
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
$value = ''; |
447
|
|
|
$current = $this->value; |
448
|
|
|
|
449
|
|
|
if ($current[0] == '-') { |
450
|
|
|
$current = substr($current, 1); |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
while (bccomp($current, '0', 0) > 0) { |
454
|
|
|
$temp = bcmod($current, '16777216'); |
455
|
|
|
$value = chr($temp >> 16).chr($temp >> 8).chr($temp).$value; |
456
|
|
|
$current = bcdiv($current, '16777216', 0); |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
return $this->precision > 0 ? |
460
|
|
|
substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : |
461
|
|
|
ltrim($value, chr(0)); |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
if (!count($this->value)) { |
465
|
|
|
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; |
466
|
|
|
} |
467
|
|
|
$result = self::_int2bytes($this->value[count($this->value) - 1]); |
468
|
|
|
|
469
|
|
|
for ($i = count($this->value) - 2; $i >= 0; --$i) { |
470
|
|
|
self::_base256_lshift($result, self::$base); |
471
|
|
|
$result = $result | str_pad(self::_int2bytes($this->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
return $this->precision > 0 ? |
475
|
|
|
str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) : |
476
|
|
|
$result; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
/** |
480
|
|
|
* Converts a BigInteger to a hex string (eg. base-16)). |
481
|
|
|
* |
482
|
|
|
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're |
483
|
|
|
* saved as two's compliment. |
484
|
|
|
* |
485
|
|
|
* Here's an example: |
486
|
|
|
* <code> |
487
|
|
|
* <?php |
488
|
|
|
* $a = new \Jose\Util\ger('65'); |
489
|
|
|
* |
490
|
|
|
* echo $a->toHex(); // outputs '41' |
491
|
|
|
* ?> |
492
|
|
|
* </code> |
493
|
|
|
* |
494
|
|
|
* @param bool $twos_compliment |
495
|
|
|
* |
496
|
|
|
* @return string |
497
|
|
|
* |
498
|
|
|
* @internal Converts a base-2**26 number to base-2**8 |
499
|
|
|
*/ |
500
|
|
|
public function toHex($twos_compliment = false) |
501
|
|
|
{ |
502
|
|
|
return bin2hex($this->toBytes($twos_compliment)); |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
/** |
506
|
|
|
* Converts a BigInteger to a bit string (eg. base-2). |
507
|
|
|
* |
508
|
|
|
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're |
509
|
|
|
* saved as two's compliment. |
510
|
|
|
* |
511
|
|
|
* Here's an example: |
512
|
|
|
* <code> |
513
|
|
|
* <?php |
514
|
|
|
* $a = new \Jose\Util\ger('65'); |
515
|
|
|
* |
516
|
|
|
* echo $a->toBits(); // outputs '1000001' |
517
|
|
|
* ?> |
518
|
|
|
* </code> |
519
|
|
|
* |
520
|
|
|
* @param bool $twos_compliment |
521
|
|
|
* |
522
|
|
|
* @return string |
523
|
|
|
* |
524
|
|
|
* @internal Converts a base-2**26 number to base-2**2 |
525
|
|
|
*/ |
526
|
|
|
public function toBits($twos_compliment = false) |
527
|
|
|
{ |
528
|
|
|
$hex = $this->toHex($twos_compliment); |
529
|
|
|
$bits = ''; |
530
|
|
|
for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i -= 8) { |
531
|
|
|
$bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT).$bits; |
532
|
|
|
} |
533
|
|
|
if ($start) { // hexdec('') == 0 |
534
|
|
|
$bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT).$bits; |
535
|
|
|
} |
536
|
|
|
$result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); |
537
|
|
|
|
538
|
|
|
if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) { |
539
|
|
|
return '0'.$result; |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
return $result; |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
/** |
546
|
|
|
* Converts a BigInteger to a base-10 number. |
547
|
|
|
* |
548
|
|
|
* Here's an example: |
549
|
|
|
* <code> |
550
|
|
|
* <?php |
551
|
|
|
* $a = new \Jose\Util\ger('50'); |
552
|
|
|
* |
553
|
|
|
* echo $a->toString(); // outputs 50 |
554
|
|
|
* ?> |
555
|
|
|
* </code> |
556
|
|
|
* |
557
|
|
|
* @return string |
558
|
|
|
* |
559
|
|
|
* @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10) |
560
|
|
|
*/ |
561
|
|
|
public function toString() |
562
|
|
|
{ |
563
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
564
|
|
|
case self::MODE_GMP: |
565
|
|
|
return gmp_strval($this->value); |
566
|
|
|
case self::MODE_BCMATH: |
567
|
|
|
if ($this->value === '0') { |
568
|
|
|
return '0'; |
569
|
|
|
} |
570
|
|
|
|
571
|
|
|
return ltrim($this->value, '0'); |
572
|
|
|
} |
573
|
|
|
|
574
|
|
|
if (!count($this->value)) { |
575
|
|
|
return '0'; |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
$temp = clone $this; |
579
|
|
|
$temp->is_negative = false; |
580
|
|
|
|
581
|
|
|
$divisor = new static(); |
582
|
|
|
$divisor->value = [self::$max10]; |
583
|
|
|
$result = ''; |
584
|
|
|
while (count($temp->value)) { |
585
|
|
|
list($temp, $mod) = $temp->divide($divisor); |
586
|
|
|
$result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT).$result; |
587
|
|
|
} |
588
|
|
|
$result = ltrim($result, '0'); |
589
|
|
|
if (empty($result)) { |
590
|
|
|
$result = '0'; |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
if ($this->is_negative) { |
594
|
|
|
$result = '-'.$result; |
595
|
|
|
} |
596
|
|
|
|
597
|
|
|
return $result; |
598
|
|
|
} |
599
|
|
|
|
600
|
|
|
/** |
601
|
|
|
* __toString() magic method. |
602
|
|
|
* |
603
|
|
|
* Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call |
604
|
|
|
* toString(). |
605
|
|
|
* |
606
|
|
|
* @internal Implemented per a suggestion by Techie-Michael - thanks! |
607
|
|
|
*/ |
608
|
|
|
public function __toString() |
609
|
|
|
{ |
610
|
|
|
return $this->toString(); |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
/** |
614
|
|
|
* __sleep() magic method. |
615
|
|
|
* |
616
|
|
|
* Will be called, automatically, when serialize() is called on a BigInteger object. |
617
|
|
|
*/ |
618
|
|
|
public function __sleep() |
619
|
|
|
{ |
620
|
|
|
$this->hex = $this->toHex(true); |
621
|
|
|
$vars = ['hex']; |
622
|
|
|
if ($this->precision > 0) { |
623
|
|
|
$vars[] = 'precision'; |
624
|
|
|
} |
625
|
|
|
|
626
|
|
|
return $vars; |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
/** |
630
|
|
|
* __wakeup() magic method. |
631
|
|
|
* |
632
|
|
|
* Will be called, automatically, when unserialize() is called on a BigInteger object. |
633
|
|
|
*/ |
634
|
|
|
private function __wakeup() |
635
|
|
|
{ |
636
|
|
|
$temp = new static($this->hex, -16); |
637
|
|
|
$this->value = $temp->value; |
638
|
|
|
$this->is_negative = $temp->is_negative; |
639
|
|
|
if ($this->precision > 0) { |
640
|
|
|
// recalculate $this->bitmask |
641
|
|
|
$this->setPrecision($this->precision); |
642
|
|
|
} |
643
|
|
|
} |
644
|
|
|
|
645
|
|
|
/** |
646
|
|
|
* __debugInfo() magic method. |
647
|
|
|
* |
648
|
|
|
* Will be called, automatically, when print_r() or var_dump() are called |
649
|
|
|
*/ |
650
|
|
|
public function __debugInfo() |
651
|
|
|
{ |
652
|
|
|
$opts = []; |
653
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
654
|
|
|
case self::MODE_GMP: |
655
|
|
|
$engine = 'gmp'; |
656
|
|
|
break; |
657
|
|
|
case self::MODE_BCMATH: |
658
|
|
|
$engine = 'bcmath'; |
659
|
|
|
break; |
660
|
|
|
case self::MODE_INTERNAL: |
661
|
|
|
$engine = 'internal'; |
662
|
|
|
$opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit'; |
663
|
|
|
} |
664
|
|
|
if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { |
665
|
|
|
$opts[] = 'OpenSSL'; |
666
|
|
|
} |
667
|
|
|
if (!empty($opts)) { |
668
|
|
|
$engine .= ' ('.implode($opts, ', ').')'; |
|
|
|
|
669
|
|
|
} |
670
|
|
|
|
671
|
|
|
return [ |
672
|
|
|
'value' => '0x'.$this->toHex(true), |
673
|
|
|
'engine' => $engine, |
674
|
|
|
]; |
675
|
|
|
} |
676
|
|
|
|
677
|
|
|
/** |
678
|
|
|
* Adds two BigIntegers. |
679
|
|
|
* |
680
|
|
|
* Here's an example: |
681
|
|
|
* <code> |
682
|
|
|
* <?php |
683
|
|
|
* $a = new \Jose\Util\ger('10'); |
684
|
|
|
* $b = new \Jose\Util\ger('20'); |
685
|
|
|
* |
686
|
|
|
* $c = $a->add($b); |
687
|
|
|
* |
688
|
|
|
* echo $c->toString(); // outputs 30 |
689
|
|
|
* ?> |
690
|
|
|
* </code> |
691
|
|
|
* |
692
|
|
|
* @param \Jose\Util\Integer $y |
693
|
|
|
* |
694
|
|
|
* @return \Jose\Util\BigInteger |
695
|
|
|
* |
696
|
|
|
* @internal Performs base-2**52 addition |
697
|
|
|
*/ |
698
|
|
|
public function add(BigInteger $y) |
699
|
|
|
{ |
700
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
701
|
|
|
case self::MODE_GMP: |
702
|
|
|
$temp = new static(); |
703
|
|
|
$temp->value = gmp_add($this->value, $y->value); |
|
|
|
|
704
|
|
|
|
705
|
|
|
return $this->_normalize($temp); |
706
|
|
|
case self::MODE_BCMATH: |
707
|
|
|
$temp = new static(); |
708
|
|
|
$temp->value = bcadd($this->value, $y->value, 0); |
|
|
|
|
709
|
|
|
|
710
|
|
|
return $this->_normalize($temp); |
711
|
|
|
} |
712
|
|
|
|
713
|
|
|
$temp = self::_add($this->value, $this->is_negative, $y->value, $y->is_negative); |
714
|
|
|
|
715
|
|
|
$result = new static(); |
716
|
|
|
$result->value = $temp[self::VALUE]; |
|
|
|
|
717
|
|
|
$result->is_negative = $temp[self::SIGN]; |
|
|
|
|
718
|
|
|
|
719
|
|
|
return $this->_normalize($result); |
720
|
|
|
} |
721
|
|
|
|
722
|
|
|
/** |
723
|
|
|
* Performs addition. |
724
|
|
|
* |
725
|
|
|
* @param array $x_value |
726
|
|
|
* @param bool $x_negative |
727
|
|
|
* @param array $y_value |
728
|
|
|
* @param bool $y_negative |
729
|
|
|
* |
730
|
|
|
* @return array |
731
|
|
|
*/ |
732
|
|
|
private static function _add($x_value, $x_negative, $y_value, $y_negative) |
733
|
|
|
{ |
734
|
|
|
$x_size = count($x_value); |
735
|
|
|
$y_size = count($y_value); |
736
|
|
|
|
737
|
|
|
if ($x_size == 0) { |
738
|
|
|
return [ |
739
|
|
|
self::VALUE => $y_value, |
740
|
|
|
self::SIGN => $y_negative, |
741
|
|
|
]; |
742
|
|
|
} elseif ($y_size == 0) { |
743
|
|
|
return [ |
744
|
|
|
self::VALUE => $x_value, |
745
|
|
|
self::SIGN => $x_negative, |
746
|
|
|
]; |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
// subtract, if appropriate |
750
|
|
|
if ($x_negative != $y_negative) { |
751
|
|
|
if ($x_value == $y_value) { |
752
|
|
|
return [ |
753
|
|
|
self::VALUE => [], |
754
|
|
|
self::SIGN => false, |
755
|
|
|
]; |
756
|
|
|
} |
757
|
|
|
|
758
|
|
|
$temp = self::_subtract($x_value, false, $y_value, false); |
759
|
|
|
$temp[self::SIGN] = self::_compare($x_value, false, $y_value, false) > 0 ? |
760
|
|
|
$x_negative : $y_negative; |
761
|
|
|
|
762
|
|
|
return $temp; |
763
|
|
|
} |
764
|
|
|
|
765
|
|
|
if ($x_size < $y_size) { |
766
|
|
|
$size = $x_size; |
767
|
|
|
$value = $y_value; |
768
|
|
|
} else { |
769
|
|
|
$size = $y_size; |
770
|
|
|
$value = $x_value; |
771
|
|
|
} |
772
|
|
|
|
773
|
|
|
$value[count($value)] = 0; // just in case the carry adds an extra digit |
774
|
|
|
|
775
|
|
|
$carry = 0; |
776
|
|
|
for ($i = 0, $j = 1; $j < $size; $i += 2, $j += 2) { |
777
|
|
|
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry; |
778
|
|
|
$carry = $sum >= self::$maxDigit2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 |
779
|
|
|
$sum = $carry ? $sum - self::$maxDigit2 : $sum; |
780
|
|
|
|
781
|
|
|
$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); |
782
|
|
|
|
783
|
|
|
$value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) |
784
|
|
|
$value[$j] = $temp; |
785
|
|
|
} |
786
|
|
|
|
787
|
|
|
if ($j == $size) { // ie. if $y_size is odd |
788
|
|
|
$sum = $x_value[$i] + $y_value[$i] + $carry; |
789
|
|
|
$carry = $sum >= self::$baseFull; |
790
|
|
|
$value[$i] = $carry ? $sum - self::$baseFull : $sum; |
791
|
|
|
++$i; // ie. let $i = $j since we've just done $value[$i] |
792
|
|
|
} |
793
|
|
|
|
794
|
|
|
if ($carry) { |
795
|
|
|
for (; $value[$i] == self::$maxDigit; ++$i) { |
796
|
|
|
$value[$i] = 0; |
797
|
|
|
} |
798
|
|
|
++$value[$i]; |
799
|
|
|
} |
800
|
|
|
|
801
|
|
|
return [ |
802
|
|
|
self::VALUE => self::_trim($value), |
803
|
|
|
self::SIGN => $x_negative, |
804
|
|
|
]; |
805
|
|
|
} |
806
|
|
|
|
807
|
|
|
/** |
808
|
|
|
* Subtracts two BigIntegers. |
809
|
|
|
* |
810
|
|
|
* Here's an example: |
811
|
|
|
* <code> |
812
|
|
|
* <?php |
813
|
|
|
* $a = new \Jose\Util\ger('10'); |
814
|
|
|
* $b = new \Jose\Util\ger('20'); |
815
|
|
|
* |
816
|
|
|
* $c = $a->subtract($b); |
817
|
|
|
* |
818
|
|
|
* echo $c->toString(); // outputs -10 |
819
|
|
|
* ?> |
820
|
|
|
* </code> |
821
|
|
|
* |
822
|
|
|
* @param \Jose\Util\Integer $y |
823
|
|
|
* |
824
|
|
|
* @return \Jose\Util\BigInteger |
825
|
|
|
* |
826
|
|
|
* @internal Performs base-2**52 subtraction |
827
|
|
|
*/ |
828
|
|
|
public function subtract(BigInteger $y) |
829
|
|
|
{ |
830
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
831
|
|
|
case self::MODE_GMP: |
832
|
|
|
$temp = new static(); |
833
|
|
|
$temp->value = gmp_sub($this->value, $y->value); |
|
|
|
|
834
|
|
|
|
835
|
|
|
return $this->_normalize($temp); |
836
|
|
|
case self::MODE_BCMATH: |
837
|
|
|
$temp = new static(); |
838
|
|
|
$temp->value = bcsub($this->value, $y->value, 0); |
|
|
|
|
839
|
|
|
|
840
|
|
|
return $this->_normalize($temp); |
841
|
|
|
} |
842
|
|
|
|
843
|
|
|
$temp = self::_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); |
844
|
|
|
|
845
|
|
|
$result = new static(); |
846
|
|
|
$result->value = $temp[self::VALUE]; |
|
|
|
|
847
|
|
|
$result->is_negative = $temp[self::SIGN]; |
|
|
|
|
848
|
|
|
|
849
|
|
|
return $this->_normalize($result); |
850
|
|
|
} |
851
|
|
|
|
852
|
|
|
/** |
853
|
|
|
* Performs subtraction. |
854
|
|
|
* |
855
|
|
|
* @param array $x_value |
856
|
|
|
* @param bool $x_negative |
857
|
|
|
* @param array $y_value |
858
|
|
|
* @param bool $y_negative |
859
|
|
|
* |
860
|
|
|
* @return array |
861
|
|
|
*/ |
862
|
|
|
private static function _subtract($x_value, $x_negative, $y_value, $y_negative) |
863
|
|
|
{ |
864
|
|
|
$x_size = count($x_value); |
865
|
|
|
$y_size = count($y_value); |
866
|
|
|
|
867
|
|
|
if ($x_size == 0) { |
868
|
|
|
return [ |
869
|
|
|
self::VALUE => $y_value, |
870
|
|
|
self::SIGN => !$y_negative, |
871
|
|
|
]; |
872
|
|
|
} elseif ($y_size == 0) { |
873
|
|
|
return [ |
874
|
|
|
self::VALUE => $x_value, |
875
|
|
|
self::SIGN => $x_negative, |
876
|
|
|
]; |
877
|
|
|
} |
878
|
|
|
|
879
|
|
|
// add, if appropriate (ie. -$x - +$y or +$x - -$y) |
880
|
|
|
if ($x_negative != $y_negative) { |
881
|
|
|
$temp = self::_add($x_value, false, $y_value, false); |
882
|
|
|
$temp[self::SIGN] = $x_negative; |
883
|
|
|
|
884
|
|
|
return $temp; |
885
|
|
|
} |
886
|
|
|
|
887
|
|
|
$diff = self::_compare($x_value, $x_negative, $y_value, $y_negative); |
888
|
|
|
|
889
|
|
|
if (!$diff) { |
890
|
|
|
return [ |
891
|
|
|
self::VALUE => [], |
892
|
|
|
self::SIGN => false, |
893
|
|
|
]; |
894
|
|
|
} |
895
|
|
|
|
896
|
|
|
// switch $x and $y around, if appropriate. |
897
|
|
|
if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) { |
898
|
|
|
$temp = $x_value; |
899
|
|
|
$x_value = $y_value; |
900
|
|
|
$y_value = $temp; |
901
|
|
|
|
902
|
|
|
$x_negative = !$x_negative; |
903
|
|
|
|
904
|
|
|
$x_size = count($x_value); |
|
|
|
|
905
|
|
|
$y_size = count($y_value); |
906
|
|
|
} |
907
|
|
|
|
908
|
|
|
// at this point, $x_value should be at least as big as - if not bigger than - $y_value |
909
|
|
|
|
910
|
|
|
$carry = 0; |
911
|
|
|
for ($i = 0, $j = 1; $j < $y_size; $i += 2, $j += 2) { |
912
|
|
|
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry; |
913
|
|
|
$carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 |
914
|
|
|
$sum = $carry ? $sum + self::$maxDigit2 : $sum; |
915
|
|
|
|
916
|
|
|
$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); |
917
|
|
|
|
918
|
|
|
$x_value[$i] = (int) ($sum - self::$baseFull * $temp); |
919
|
|
|
$x_value[$j] = $temp; |
920
|
|
|
} |
921
|
|
|
|
922
|
|
|
if ($j == $y_size) { // ie. if $y_size is odd |
923
|
|
|
$sum = $x_value[$i] - $y_value[$i] - $carry; |
924
|
|
|
$carry = $sum < 0; |
925
|
|
|
$x_value[$i] = $carry ? $sum + self::$baseFull : $sum; |
926
|
|
|
++$i; |
927
|
|
|
} |
928
|
|
|
|
929
|
|
|
if ($carry) { |
930
|
|
|
for (; !$x_value[$i]; ++$i) { |
931
|
|
|
$x_value[$i] = self::$maxDigit; |
932
|
|
|
} |
933
|
|
|
--$x_value[$i]; |
934
|
|
|
} |
935
|
|
|
|
936
|
|
|
return [ |
937
|
|
|
self::VALUE => self::_trim($x_value), |
938
|
|
|
self::SIGN => $x_negative, |
939
|
|
|
]; |
940
|
|
|
} |
941
|
|
|
|
942
|
|
|
/** |
943
|
|
|
* Multiplies two BigIntegers. |
944
|
|
|
* |
945
|
|
|
* Here's an example: |
946
|
|
|
* <code> |
947
|
|
|
* <?php |
948
|
|
|
* $a = new \Jose\Util\ger('10'); |
949
|
|
|
* $b = new \Jose\Util\ger('20'); |
950
|
|
|
* |
951
|
|
|
* $c = $a->multiply($b); |
952
|
|
|
* |
953
|
|
|
* echo $c->toString(); // outputs 200 |
954
|
|
|
* ?> |
955
|
|
|
* </code> |
956
|
|
|
* |
957
|
|
|
* @param \Jose\Util\Integer $x |
958
|
|
|
* |
959
|
|
|
* @return \Jose\Util\BigInteger |
960
|
|
|
*/ |
961
|
|
|
public function multiply(BigInteger $x) |
962
|
|
|
{ |
963
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
964
|
|
|
case self::MODE_GMP: |
965
|
|
|
$temp = new static(); |
966
|
|
|
$temp->value = gmp_mul($this->value, $x->value); |
|
|
|
|
967
|
|
|
|
968
|
|
|
return $this->_normalize($temp); |
969
|
|
|
case self::MODE_BCMATH: |
970
|
|
|
$temp = new static(); |
971
|
|
|
$temp->value = bcmul($this->value, $x->value, 0); |
|
|
|
|
972
|
|
|
|
973
|
|
|
return $this->_normalize($temp); |
974
|
|
|
} |
975
|
|
|
|
976
|
|
|
$temp = self::_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); |
977
|
|
|
|
978
|
|
|
$product = new static(); |
979
|
|
|
$product->value = $temp[self::VALUE]; |
|
|
|
|
980
|
|
|
$product->is_negative = $temp[self::SIGN]; |
|
|
|
|
981
|
|
|
|
982
|
|
|
return $this->_normalize($product); |
983
|
|
|
} |
984
|
|
|
|
985
|
|
|
/** |
986
|
|
|
* Performs multiplication. |
987
|
|
|
* |
988
|
|
|
* @param array $x_value |
989
|
|
|
* @param bool $x_negative |
990
|
|
|
* @param array $y_value |
991
|
|
|
* @param bool $y_negative |
992
|
|
|
* |
993
|
|
|
* @return array |
994
|
|
|
*/ |
995
|
|
|
private static function _multiply($x_value, $x_negative, $y_value, $y_negative) |
996
|
|
|
{ |
997
|
|
|
//if ( $x_value == $y_value ) { |
998
|
|
|
// return array( |
999
|
|
|
// self::VALUE => $this->_square($x_value), |
1000
|
|
|
// self::SIGN => $x_sign != $y_value |
1001
|
|
|
// ); |
1002
|
|
|
//} |
1003
|
|
|
|
1004
|
|
|
$x_length = count($x_value); |
1005
|
|
|
$y_length = count($y_value); |
1006
|
|
|
|
1007
|
|
|
if (!$x_length || !$y_length) { // a 0 is being multiplied |
1008
|
|
|
return [ |
1009
|
|
|
self::VALUE => [], |
1010
|
|
|
self::SIGN => false, |
1011
|
|
|
]; |
1012
|
|
|
} |
1013
|
|
|
|
1014
|
|
|
return [ |
1015
|
|
|
self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ? |
1016
|
|
|
self::_trim(self::_regularMultiply($x_value, $y_value)) : |
1017
|
|
|
self::_trim(self::_karatsuba($x_value, $y_value)), |
|
|
|
|
1018
|
|
|
self::SIGN => $x_negative != $y_negative, |
1019
|
|
|
]; |
1020
|
|
|
} |
1021
|
|
|
|
1022
|
|
|
/** |
1023
|
|
|
* Performs long multiplication on two BigIntegers. |
1024
|
|
|
* |
1025
|
|
|
* Modeled after 'multiply' in MutableBigInteger.java. |
1026
|
|
|
* |
1027
|
|
|
* @param array $x_value |
1028
|
|
|
* @param array $y_value |
1029
|
|
|
* |
1030
|
|
|
* @return array |
1031
|
|
|
*/ |
1032
|
|
|
private static function _regularMultiply($x_value, $y_value) |
1033
|
|
|
{ |
1034
|
|
|
$x_length = count($x_value); |
1035
|
|
|
$y_length = count($y_value); |
1036
|
|
|
|
1037
|
|
|
if (!$x_length || !$y_length) { // a 0 is being multiplied |
1038
|
|
|
return []; |
1039
|
|
|
} |
1040
|
|
|
|
1041
|
|
|
if ($x_length < $y_length) { |
1042
|
|
|
$temp = $x_value; |
1043
|
|
|
$x_value = $y_value; |
1044
|
|
|
$y_value = $temp; |
1045
|
|
|
|
1046
|
|
|
$x_length = count($x_value); |
1047
|
|
|
$y_length = count($y_value); |
1048
|
|
|
} |
1049
|
|
|
|
1050
|
|
|
$product_value = self::_array_repeat(0, $x_length + $y_length); |
1051
|
|
|
|
1052
|
|
|
// the following for loop could be removed if the for loop following it |
1053
|
|
|
// (the one with nested for loops) initially set $i to 0, but |
1054
|
|
|
// doing so would also make the result in one set of unnecessary adds, |
1055
|
|
|
// since on the outermost loops first pass, $product->value[$k] is going |
1056
|
|
|
// to always be 0 |
1057
|
|
|
|
1058
|
|
|
$carry = 0; |
1059
|
|
|
|
1060
|
|
|
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0 |
1061
|
|
|
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 |
1062
|
|
|
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
1063
|
|
|
$product_value[$j] = (int) ($temp - self::$baseFull * $carry); |
1064
|
|
|
} |
1065
|
|
|
|
1066
|
|
|
$product_value[$j] = $carry; |
1067
|
|
|
|
1068
|
|
|
// the above for loop is what the previous comment was talking about. the |
1069
|
|
|
// following for loop is the "one with nested for loops" |
1070
|
|
|
for ($i = 1; $i < $y_length; ++$i) { |
1071
|
|
|
$carry = 0; |
1072
|
|
|
|
1073
|
|
|
for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { |
1074
|
|
|
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; |
1075
|
|
|
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
1076
|
|
|
$product_value[$k] = (int) ($temp - self::$baseFull * $carry); |
1077
|
|
|
} |
1078
|
|
|
|
1079
|
|
|
$product_value[$k] = $carry; |
1080
|
|
|
} |
1081
|
|
|
|
1082
|
|
|
return $product_value; |
1083
|
|
|
} |
1084
|
|
|
|
1085
|
|
|
/** |
1086
|
|
|
* Performs Karatsuba multiplication on two BigIntegers. |
1087
|
|
|
* |
1088
|
|
|
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and |
1089
|
|
|
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}. |
1090
|
|
|
* |
1091
|
|
|
* @param array $x_value |
1092
|
|
|
* @param array $y_value |
1093
|
|
|
* |
1094
|
|
|
* @return array |
1095
|
|
|
*/ |
1096
|
|
|
private static function _karatsuba($x_value, $y_value) |
1097
|
|
|
{ |
1098
|
|
|
$m = min(count($x_value) >> 1, count($y_value) >> 1); |
1099
|
|
|
|
1100
|
|
|
if ($m < self::KARATSUBA_CUTOFF) { |
1101
|
|
|
return self::_regularMultiply($x_value, $y_value); |
1102
|
|
|
} |
1103
|
|
|
|
1104
|
|
|
$x1 = array_slice($x_value, $m); |
1105
|
|
|
$x0 = array_slice($x_value, 0, $m); |
1106
|
|
|
$y1 = array_slice($y_value, $m); |
1107
|
|
|
$y0 = array_slice($y_value, 0, $m); |
1108
|
|
|
|
1109
|
|
|
$z2 = self::_karatsuba($x1, $y1); |
1110
|
|
|
$z0 = self::_karatsuba($x0, $y0); |
1111
|
|
|
|
1112
|
|
|
$z1 = self::_add($x1, false, $x0, false); |
1113
|
|
|
$temp = self::_add($y1, false, $y0, false); |
1114
|
|
|
$z1 = self::_karatsuba($z1[self::VALUE], $temp[self::VALUE]); |
|
|
|
|
1115
|
|
|
$temp = self::_add($z2, false, $z0, false); |
1116
|
|
|
$z1 = self::_subtract($z1, false, $temp[self::VALUE], false); |
|
|
|
|
1117
|
|
|
|
1118
|
|
|
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); |
1119
|
|
|
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); |
1120
|
|
|
|
1121
|
|
|
$xy = self::_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); |
|
|
|
|
1122
|
|
|
$xy = self::_add($xy[self::VALUE], $xy[self::SIGN], $z0, false); |
|
|
|
|
1123
|
|
|
|
1124
|
|
|
return $xy[self::VALUE]; |
1125
|
|
|
} |
1126
|
|
|
|
1127
|
|
|
/** |
1128
|
|
|
* Performs squaring. |
1129
|
|
|
* |
1130
|
|
|
* @param array $x |
1131
|
|
|
* |
1132
|
|
|
* @return array |
1133
|
|
|
*/ |
1134
|
|
|
private static function _square($x = false) |
1135
|
|
|
{ |
1136
|
|
|
return count($x) < 2 * self::KARATSUBA_CUTOFF ? |
1137
|
|
|
self::_trim(self::_baseSquare($x)) : |
|
|
|
|
1138
|
|
|
self::_trim(self::_karatsubaSquare($x)); |
|
|
|
|
1139
|
|
|
} |
1140
|
|
|
|
1141
|
|
|
/** |
1142
|
|
|
* Performs traditional squaring on two BigIntegers. |
1143
|
|
|
* |
1144
|
|
|
* Squaring can be done faster than multiplying a number by itself can be. See |
1145
|
|
|
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} / |
1146
|
|
|
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information. |
1147
|
|
|
* |
1148
|
|
|
* @param array $value |
1149
|
|
|
* |
1150
|
|
|
* @return array |
1151
|
|
|
*/ |
1152
|
|
|
private static function _baseSquare($value) |
1153
|
|
|
{ |
1154
|
|
|
if (empty($value)) { |
1155
|
|
|
return []; |
1156
|
|
|
} |
1157
|
|
|
$square_value = self::_array_repeat(0, 2 * count($value)); |
1158
|
|
|
|
1159
|
|
|
for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) { |
1160
|
|
|
$i2 = $i << 1; |
1161
|
|
|
|
1162
|
|
|
$temp = $square_value[$i2] + $value[$i] * $value[$i]; |
1163
|
|
|
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
1164
|
|
|
$square_value[$i2] = (int) ($temp - self::$baseFull * $carry); |
1165
|
|
|
|
1166
|
|
|
// note how we start from $i+1 instead of 0 as we do in multiplication. |
1167
|
|
|
for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { |
1168
|
|
|
$temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; |
1169
|
|
|
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
1170
|
|
|
$square_value[$k] = (int) ($temp - self::$baseFull * $carry); |
1171
|
|
|
} |
1172
|
|
|
|
1173
|
|
|
// the following line can yield values larger 2**15. at this point, PHP should switch |
1174
|
|
|
// over to floats. |
1175
|
|
|
$square_value[$i + $max_index + 1] = $carry; |
1176
|
|
|
} |
1177
|
|
|
|
1178
|
|
|
return $square_value; |
1179
|
|
|
} |
1180
|
|
|
|
1181
|
|
|
/** |
1182
|
|
|
* Performs Karatsuba "squaring" on two BigIntegers. |
1183
|
|
|
* |
1184
|
|
|
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and |
1185
|
|
|
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}. |
1186
|
|
|
* |
1187
|
|
|
* @param array $value |
1188
|
|
|
* |
1189
|
|
|
* @return array |
1190
|
|
|
*/ |
1191
|
|
|
private static function _karatsubaSquare($value) |
1192
|
|
|
{ |
1193
|
|
|
$m = count($value) >> 1; |
1194
|
|
|
|
1195
|
|
|
if ($m < self::KARATSUBA_CUTOFF) { |
1196
|
|
|
return self::_baseSquare($value); |
1197
|
|
|
} |
1198
|
|
|
|
1199
|
|
|
$x1 = array_slice($value, $m); |
1200
|
|
|
$x0 = array_slice($value, 0, $m); |
1201
|
|
|
|
1202
|
|
|
$z2 = self::_karatsubaSquare($x1); |
1203
|
|
|
$z0 = self::_karatsubaSquare($x0); |
1204
|
|
|
|
1205
|
|
|
$z1 = self::_add($x1, false, $x0, false); |
1206
|
|
|
$z1 = self::_karatsubaSquare($z1[self::VALUE]); |
|
|
|
|
1207
|
|
|
$temp = self::_add($z2, false, $z0, false); |
1208
|
|
|
$z1 = self::_subtract($z1, false, $temp[self::VALUE], false); |
|
|
|
|
1209
|
|
|
|
1210
|
|
|
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); |
1211
|
|
|
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); |
1212
|
|
|
|
1213
|
|
|
$xx = self::_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); |
|
|
|
|
1214
|
|
|
$xx = self::_add($xx[self::VALUE], $xx[self::SIGN], $z0, false); |
|
|
|
|
1215
|
|
|
|
1216
|
|
|
return $xx[self::VALUE]; |
1217
|
|
|
} |
1218
|
|
|
|
1219
|
|
|
/** |
1220
|
|
|
* Divides two BigIntegers. |
1221
|
|
|
* |
1222
|
|
|
* Returns an array whose first element contains the quotient and whose second element contains the |
1223
|
|
|
* "common residue". If the remainder would be positive, the "common residue" and the remainder are the |
1224
|
|
|
* same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder |
1225
|
|
|
* and the divisor (basically, the "common residue" is the first positive modulo). |
1226
|
|
|
* |
1227
|
|
|
* Here's an example: |
1228
|
|
|
* <code> |
1229
|
|
|
* <?php |
1230
|
|
|
* $a = new \Jose\Util\ger('10'); |
1231
|
|
|
* $b = new \Jose\Util\ger('20'); |
1232
|
|
|
* |
1233
|
|
|
* list($quotient, $remainder) = $a->divide($b); |
1234
|
|
|
* |
1235
|
|
|
* echo $quotient->toString(); // outputs 0 |
1236
|
|
|
* echo "\r\n"; |
1237
|
|
|
* echo $remainder->toString(); // outputs 10 |
1238
|
|
|
* ?> |
1239
|
|
|
* </code> |
1240
|
|
|
* |
1241
|
|
|
* @param \Jose\Util\Integer $y |
1242
|
|
|
* |
1243
|
|
|
* @return array |
1244
|
|
|
* |
1245
|
|
|
* @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}. |
1246
|
|
|
*/ |
1247
|
|
|
public function divide(BigInteger $y) |
1248
|
|
|
{ |
1249
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
1250
|
|
|
case self::MODE_GMP: |
1251
|
|
|
$quotient = new static(); |
1252
|
|
|
$remainder = new static(); |
1253
|
|
|
|
1254
|
|
|
list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value); |
1255
|
|
|
|
1256
|
|
|
if (gmp_sign($remainder->value) < 0) { |
1257
|
|
|
$remainder->value = gmp_add($remainder->value, gmp_abs($y->value)); |
|
|
|
|
1258
|
|
|
} |
1259
|
|
|
|
1260
|
|
|
return [$this->_normalize($quotient), $this->_normalize($remainder)]; |
1261
|
|
|
case self::MODE_BCMATH: |
1262
|
|
|
$quotient = new static(); |
1263
|
|
|
$remainder = new static(); |
1264
|
|
|
|
1265
|
|
|
$quotient->value = bcdiv($this->value, $y->value, 0); |
|
|
|
|
1266
|
|
|
$remainder->value = bcmod($this->value, $y->value); |
|
|
|
|
1267
|
|
|
|
1268
|
|
|
if ($remainder->value[0] == '-') { |
1269
|
|
|
$remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0); |
|
|
|
|
1270
|
|
|
} |
1271
|
|
|
|
1272
|
|
|
return [$this->_normalize($quotient), $this->_normalize($remainder)]; |
1273
|
|
|
} |
1274
|
|
|
|
1275
|
|
|
if (count($y->value) == 1) { |
1276
|
|
|
list($q, $r) = $this->_divide_digit($this->value, $y->value[0]); |
1277
|
|
|
$quotient = new static(); |
1278
|
|
|
$remainder = new static(); |
1279
|
|
|
$quotient->value = $q; |
1280
|
|
|
$remainder->value = [$r]; |
1281
|
|
|
$quotient->is_negative = $this->is_negative != $y->is_negative; |
1282
|
|
|
|
1283
|
|
|
return [$this->_normalize($quotient), $this->_normalize($remainder)]; |
1284
|
|
|
} |
1285
|
|
|
|
1286
|
|
|
static $zero; |
1287
|
|
|
if (!isset($zero)) { |
1288
|
|
|
$zero = new static(); |
1289
|
|
|
} |
1290
|
|
|
|
1291
|
|
|
$x = clone $this; |
1292
|
|
|
$y = clone $y; |
1293
|
|
|
|
1294
|
|
|
$x_sign = $x->is_negative; |
1295
|
|
|
$y_sign = $y->is_negative; |
1296
|
|
|
|
1297
|
|
|
$x->is_negative = $y->is_negative = false; |
1298
|
|
|
|
1299
|
|
|
$diff = $x->compare($y); |
1300
|
|
|
|
1301
|
|
|
if (!$diff) { |
1302
|
|
|
$temp = new static(); |
1303
|
|
|
$temp->value = [1]; |
1304
|
|
|
$temp->is_negative = $x_sign != $y_sign; |
1305
|
|
|
|
1306
|
|
|
return [$this->_normalize($temp), $this->_normalize(new static())]; |
1307
|
|
|
} |
1308
|
|
|
|
1309
|
|
|
if ($diff < 0) { |
1310
|
|
|
// if $x is negative, "add" $y. |
1311
|
|
|
if ($x_sign) { |
1312
|
|
|
$x = $y->subtract($x); |
1313
|
|
|
} |
1314
|
|
|
|
1315
|
|
|
return [$this->_normalize(new static()), $this->_normalize($x)]; |
1316
|
|
|
} |
1317
|
|
|
|
1318
|
|
|
// normalize $x and $y as described in HAC 14.23 / 14.24 |
1319
|
|
|
$msb = $y->value[count($y->value) - 1]; |
1320
|
|
|
for ($shift = 0; !($msb & self::$msb); ++$shift) { |
1321
|
|
|
$msb <<= 1; |
1322
|
|
|
} |
1323
|
|
|
$x->_lshift($shift); |
1324
|
|
|
$y->_lshift($shift); |
1325
|
|
|
$y_value = &$y->value; |
1326
|
|
|
|
1327
|
|
|
$x_max = count($x->value) - 1; |
1328
|
|
|
$y_max = count($y->value) - 1; |
1329
|
|
|
|
1330
|
|
|
$quotient = new static(); |
1331
|
|
|
$quotient_value = &$quotient->value; |
1332
|
|
|
$quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1); |
1333
|
|
|
|
1334
|
|
|
static $temp, $lhs, $rhs; |
1335
|
|
|
if (!isset($temp)) { |
1336
|
|
|
$temp = new static(); |
1337
|
|
|
$lhs = new static(); |
1338
|
|
|
$rhs = new static(); |
1339
|
|
|
} |
1340
|
|
|
$temp_value = &$temp->value; |
1341
|
|
|
$rhs_value = &$rhs->value; |
1342
|
|
|
|
1343
|
|
|
// $temp = $y << ($x_max - $y_max-1) in base 2**26 |
1344
|
|
|
$temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value); |
1345
|
|
|
|
1346
|
|
|
while ($x->compare($temp) >= 0) { |
1347
|
|
|
// calculate the "common residue" |
1348
|
|
|
++$quotient_value[$x_max - $y_max]; |
1349
|
|
|
$x = $x->subtract($temp); |
1350
|
|
|
$x_max = count($x->value) - 1; |
1351
|
|
|
} |
1352
|
|
|
|
1353
|
|
|
for ($i = $x_max; $i >= $y_max + 1; --$i) { |
1354
|
|
|
$x_value = &$x->value; |
1355
|
|
|
$x_window = [ |
1356
|
|
|
isset($x_value[$i]) ? $x_value[$i] : 0, |
1357
|
|
|
isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0, |
1358
|
|
|
isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0, |
1359
|
|
|
]; |
1360
|
|
|
$y_window = [ |
1361
|
|
|
$y_value[$y_max], |
1362
|
|
|
($y_max > 0) ? $y_value[$y_max - 1] : 0, |
1363
|
|
|
]; |
1364
|
|
|
|
1365
|
|
|
$q_index = $i - $y_max - 1; |
1366
|
|
|
if ($x_window[0] == $y_window[0]) { |
1367
|
|
|
$quotient_value[$q_index] = self::$maxDigit; |
1368
|
|
|
} else { |
1369
|
|
|
$quotient_value[$q_index] = $this->_safe_divide( |
1370
|
|
|
$x_window[0] * self::$baseFull + $x_window[1], |
1371
|
|
|
$y_window[0] |
1372
|
|
|
); |
1373
|
|
|
} |
1374
|
|
|
|
1375
|
|
|
$temp_value = [$y_window[1], $y_window[0]]; |
1376
|
|
|
|
1377
|
|
|
$lhs->value = [$quotient_value[$q_index]]; |
1378
|
|
|
$lhs = $lhs->multiply($temp); |
1379
|
|
|
|
1380
|
|
|
$rhs_value = [$x_window[2], $x_window[1], $x_window[0]]; |
1381
|
|
|
|
1382
|
|
|
while ($lhs->compare($rhs) > 0) { |
1383
|
|
|
--$quotient_value[$q_index]; |
1384
|
|
|
|
1385
|
|
|
$lhs->value = [$quotient_value[$q_index]]; |
1386
|
|
|
$lhs = $lhs->multiply($temp); |
1387
|
|
|
} |
1388
|
|
|
|
1389
|
|
|
$adjust = $this->_array_repeat(0, $q_index); |
1390
|
|
|
$temp_value = [$quotient_value[$q_index]]; |
1391
|
|
|
$temp = $temp->multiply($y); |
1392
|
|
|
$temp_value = &$temp->value; |
1393
|
|
|
$temp_value = array_merge($adjust, $temp_value); |
1394
|
|
|
|
1395
|
|
|
$x = $x->subtract($temp); |
1396
|
|
|
|
1397
|
|
|
if ($x->compare($zero) < 0) { |
1398
|
|
|
$temp_value = array_merge($adjust, $y_value); |
1399
|
|
|
$x = $x->add($temp); |
1400
|
|
|
|
1401
|
|
|
--$quotient_value[$q_index]; |
1402
|
|
|
} |
1403
|
|
|
|
1404
|
|
|
$x_max = count($x_value) - 1; |
|
|
|
|
1405
|
|
|
} |
1406
|
|
|
|
1407
|
|
|
// unnormalize the remainder |
1408
|
|
|
$x->_rshift($shift); |
1409
|
|
|
|
1410
|
|
|
$quotient->is_negative = $x_sign != $y_sign; |
1411
|
|
|
|
1412
|
|
|
// calculate the "common residue", if appropriate |
1413
|
|
|
if ($x_sign) { |
1414
|
|
|
$y->_rshift($shift); |
1415
|
|
|
$x = $y->subtract($x); |
1416
|
|
|
} |
1417
|
|
|
|
1418
|
|
|
return [$this->_normalize($quotient), $this->_normalize($x)]; |
1419
|
|
|
} |
1420
|
|
|
|
1421
|
|
|
/** |
1422
|
|
|
* Divides a BigInteger by a regular integer. |
1423
|
|
|
* |
1424
|
|
|
* abc / x = a00 / x + b0 / x + c / x |
1425
|
|
|
* |
1426
|
|
|
* @param array $dividend |
1427
|
|
|
* @param array $divisor |
1428
|
|
|
* |
1429
|
|
|
* @return array |
1430
|
|
|
*/ |
1431
|
|
|
private static function _divide_digit($dividend, $divisor) |
1432
|
|
|
{ |
1433
|
|
|
$carry = 0; |
1434
|
|
|
$result = []; |
1435
|
|
|
|
1436
|
|
|
for ($i = count($dividend) - 1; $i >= 0; --$i) { |
1437
|
|
|
$temp = self::$baseFull * $carry + $dividend[$i]; |
1438
|
|
|
$result[$i] = self::_safe_divide($temp, $divisor); |
|
|
|
|
1439
|
|
|
$carry = (int) ($temp - $divisor * $result[$i]); |
1440
|
|
|
} |
1441
|
|
|
|
1442
|
|
|
return [$result, $carry]; |
1443
|
|
|
} |
1444
|
|
|
|
1445
|
|
|
/** |
1446
|
|
|
* Performs modular exponentiation. |
1447
|
|
|
* |
1448
|
|
|
* Here's an example: |
1449
|
|
|
* <code> |
1450
|
|
|
* <?php |
1451
|
|
|
* $a = new \Jose\Util\ger('10'); |
1452
|
|
|
* $b = new \Jose\Util\ger('20'); |
1453
|
|
|
* $c = new \Jose\Util\ger('30'); |
1454
|
|
|
* |
1455
|
|
|
* $c = $a->modPow($b, $c); |
1456
|
|
|
* |
1457
|
|
|
* echo $c->toString(); // outputs 10 |
1458
|
|
|
* ?> |
1459
|
|
|
* </code> |
1460
|
|
|
* |
1461
|
|
|
* @param \Jose\Util\Integer $e |
1462
|
|
|
* @param \Jose\Util\Integer $n |
1463
|
|
|
* |
1464
|
|
|
* @return \Jose\Util\BigInteger |
1465
|
|
|
* |
1466
|
|
|
* @internal The most naive approach to modular exponentiation has very unreasonable requirements, and |
1467
|
|
|
* and although the approach involving repeated squaring does vastly better, it, too, is impractical |
1468
|
|
|
* for our purposes. The reason being that division - by far the most complicated and time-consuming |
1469
|
|
|
* of the basic operations (eg. +,-,*,/) - occurs multiple times within it. |
1470
|
|
|
* |
1471
|
|
|
* Modular reductions resolve this issue. Although an individual modular reduction takes more time |
1472
|
|
|
* then an individual division, when performed in succession (with the same modulo), they're a lot faster. |
1473
|
|
|
* |
1474
|
|
|
* The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction, |
1475
|
|
|
* although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the |
1476
|
|
|
* base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because |
1477
|
|
|
* the product of two odd numbers is odd), but what about when RSA isn't used? |
1478
|
|
|
* |
1479
|
|
|
* In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a |
1480
|
|
|
* Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the |
1481
|
|
|
* modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however, |
1482
|
|
|
* uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and |
1483
|
|
|
* the other, a power of two - and recombine them, later. This is the method that this modPow function uses. |
1484
|
|
|
* {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates. |
1485
|
|
|
*/ |
1486
|
|
|
public function modPow(BigInteger $e, BigInteger $n) |
1487
|
|
|
{ |
1488
|
|
|
$n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); |
|
|
|
|
1489
|
|
|
|
1490
|
|
|
if ($e->compare(new static()) < 0) { |
1491
|
|
|
$e = $e->abs(); |
1492
|
|
|
|
1493
|
|
|
$temp = $this->modInverse($n); |
|
|
|
|
1494
|
|
|
if ($temp === false) { |
1495
|
|
|
return false; |
|
|
|
|
1496
|
|
|
} |
1497
|
|
|
|
1498
|
|
|
return $this->_normalize($temp->modPow($e, $n)); |
|
|
|
|
1499
|
|
|
} |
1500
|
|
|
|
1501
|
|
|
if (MATH_BIGINTEGER_MODE == self::MODE_GMP) { |
1502
|
|
|
$temp = new static(); |
1503
|
|
|
$temp->value = gmp_powm($this->value, $e->value, $n->value); |
|
|
|
|
1504
|
|
|
|
1505
|
|
|
return $this->_normalize($temp); |
1506
|
|
|
} |
1507
|
|
|
|
1508
|
|
|
if ($this->compare(new static()) < 0 || $this->compare($n) > 0) { |
|
|
|
|
1509
|
|
|
list(, $temp) = $this->divide($n); |
|
|
|
|
1510
|
|
|
|
1511
|
|
|
return $temp->modPow($e, $n); |
|
|
|
|
1512
|
|
|
} |
1513
|
|
|
|
1514
|
|
|
if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { |
1515
|
|
|
$components = [ |
1516
|
|
|
'modulus' => $n->toBytes(true), |
1517
|
|
|
'publicExponent' => $e->toBytes(true), |
1518
|
|
|
]; |
1519
|
|
|
|
1520
|
|
|
$components = [ |
1521
|
|
|
'modulus' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), |
1522
|
|
|
'publicExponent' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']), |
1523
|
|
|
]; |
1524
|
|
|
|
1525
|
|
|
$RSAPublicKey = pack( |
1526
|
|
|
'Ca*a*a*', |
1527
|
|
|
48, |
1528
|
|
|
self::_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), |
1529
|
|
|
$components['modulus'], |
1530
|
|
|
$components['publicExponent'] |
1531
|
|
|
); |
1532
|
|
|
|
1533
|
|
|
$rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA |
1534
|
|
|
$RSAPublicKey = chr(0).$RSAPublicKey; |
1535
|
|
|
$RSAPublicKey = chr(3).self::_encodeASN1Length(strlen($RSAPublicKey)).$RSAPublicKey; |
1536
|
|
|
|
1537
|
|
|
$encapsulated = pack( |
1538
|
|
|
'Ca*a*', |
1539
|
|
|
48, |
1540
|
|
|
self::_encodeASN1Length(strlen($rsaOID.$RSAPublicKey)), |
1541
|
|
|
$rsaOID.$RSAPublicKey |
1542
|
|
|
); |
1543
|
|
|
|
1544
|
|
|
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n". |
1545
|
|
|
chunk_split(base64_encode($encapsulated)). |
1546
|
|
|
'-----END PUBLIC KEY-----'; |
1547
|
|
|
|
1548
|
|
|
$plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT); |
1549
|
|
|
|
1550
|
|
|
if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) { |
1551
|
|
|
return new static($result, 256); |
1552
|
|
|
} |
1553
|
|
|
} |
1554
|
|
|
|
1555
|
|
|
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { |
1556
|
|
|
$temp = new static(); |
1557
|
|
|
$temp->value = bcpowmod($this->value, $e->value, $n->value, 0); |
|
|
|
|
1558
|
|
|
|
1559
|
|
|
return $this->_normalize($temp); |
1560
|
|
|
} |
1561
|
|
|
|
1562
|
|
|
if (empty($e->value)) { |
1563
|
|
|
$temp = new static(); |
1564
|
|
|
$temp->value = [1]; |
1565
|
|
|
|
1566
|
|
|
return $this->_normalize($temp); |
1567
|
|
|
} |
1568
|
|
|
|
1569
|
|
|
if ($e->value == [1]) { |
1570
|
|
|
list(, $temp) = $this->divide($n); |
|
|
|
|
1571
|
|
|
|
1572
|
|
|
return $this->_normalize($temp); |
1573
|
|
|
} |
1574
|
|
|
|
1575
|
|
|
if ($e->value == [2]) { |
1576
|
|
|
$temp = new static(); |
1577
|
|
|
$temp->value = self::_square($this->value); |
1578
|
|
|
list(, $temp) = $temp->divide($n); |
|
|
|
|
1579
|
|
|
|
1580
|
|
|
return $this->_normalize($temp); |
1581
|
|
|
} |
1582
|
|
|
|
1583
|
|
|
return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT)); |
|
|
|
|
1584
|
|
|
|
1585
|
|
|
// the following code, although not callable, can be run independently of the above code |
1586
|
|
|
// although the above code performed better in my benchmarks the following could might |
1587
|
|
|
// perform better under different circumstances. in lieu of deleting it it's just been |
1588
|
|
|
// made uncallable |
1589
|
|
|
|
1590
|
|
|
// is the modulo odd? |
1591
|
|
|
if ($n->value[0] & 1) { |
|
|
|
|
1592
|
|
|
return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY)); |
1593
|
|
|
} |
1594
|
|
|
// if it's not, it's even |
1595
|
|
|
|
1596
|
|
|
// find the lowest set bit (eg. the max pow of 2 that divides $n) |
1597
|
|
|
for ($i = 0; $i < count($n->value); ++$i) { |
|
|
|
|
1598
|
|
|
if ($n->value[$i]) { |
1599
|
|
|
$temp = decbin($n->value[$i]); |
1600
|
|
|
$j = strlen($temp) - strrpos($temp, '1') - 1; |
1601
|
|
|
$j += 26 * $i; |
1602
|
|
|
break; |
1603
|
|
|
} |
1604
|
|
|
} |
1605
|
|
|
// at this point, 2^$j * $n/(2^$j) == $n |
1606
|
|
|
|
1607
|
|
|
$mod1 = clone $n; |
1608
|
|
|
$mod1->_rshift($j); |
1609
|
|
|
$mod2 = new static(); |
1610
|
|
|
$mod2->value = [1]; |
1611
|
|
|
$mod2->_lshift($j); |
1612
|
|
|
|
1613
|
|
|
$part1 = ($mod1->value != [1]) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static(); |
1614
|
|
|
$part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2); |
|
|
|
|
1615
|
|
|
|
1616
|
|
|
$y1 = $mod2->modInverse($mod1); |
1617
|
|
|
$y2 = $mod1->modInverse($mod2); |
1618
|
|
|
|
1619
|
|
|
$result = $part1->multiply($mod2); |
1620
|
|
|
$result = $result->multiply($y1); |
|
|
|
|
1621
|
|
|
|
1622
|
|
|
$temp = $part2->multiply($mod1); |
1623
|
|
|
$temp = $temp->multiply($y2); |
1624
|
|
|
|
1625
|
|
|
$result = $result->add($temp); |
1626
|
|
|
list(, $result) = $result->divide($n); |
1627
|
|
|
|
1628
|
|
|
return $this->_normalize($result); |
1629
|
|
|
} |
1630
|
|
|
|
1631
|
|
|
/** |
1632
|
|
|
* Performs modular exponentiation. |
1633
|
|
|
* |
1634
|
|
|
* Alias for modPow(). |
1635
|
|
|
* |
1636
|
|
|
* @param \Jose\Util\Integer $e |
1637
|
|
|
* @param \Jose\Util\Integer $n |
1638
|
|
|
* |
1639
|
|
|
* @return \Jose\Util\BigInteger |
1640
|
|
|
*/ |
1641
|
|
|
public function powMod(BigInteger $e, BigInteger $n) |
1642
|
|
|
{ |
1643
|
|
|
return $this->modPow($e, $n); |
1644
|
|
|
} |
1645
|
|
|
|
1646
|
|
|
/** |
1647
|
|
|
* Sliding Window k-ary Modular Exponentiation. |
1648
|
|
|
* |
1649
|
|
|
* Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} / |
1650
|
|
|
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims, |
1651
|
|
|
* however, this function performs a modular reduction after every multiplication and squaring operation. |
1652
|
|
|
* As such, this function has the same preconditions that the reductions being used do. |
1653
|
|
|
* |
1654
|
|
|
* @param \Jose\Util\Integer $e |
1655
|
|
|
* @param \Jose\Util\Integer $n |
1656
|
|
|
* @param int $mode |
1657
|
|
|
* |
1658
|
|
|
* @return \Jose\Util\BigInteger |
1659
|
|
|
*/ |
1660
|
|
|
private function _slidingWindow($e, $n, $mode) |
1661
|
|
|
{ |
1662
|
|
|
static $window_ranges = [7, 25, 81, 241, 673, 1793]; // from BigInteger.java's oddModPow function |
1663
|
|
|
//static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1 |
1664
|
|
|
|
1665
|
|
|
$e_value = $e->value; |
1666
|
|
|
$e_length = count($e_value) - 1; |
1667
|
|
|
$e_bits = decbin($e_value[$e_length]); |
1668
|
|
|
for ($i = $e_length - 1; $i >= 0; --$i) { |
1669
|
|
|
$e_bits .= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT); |
1670
|
|
|
} |
1671
|
|
|
|
1672
|
|
|
$e_length = strlen($e_bits); |
1673
|
|
|
|
1674
|
|
|
// calculate the appropriate window size. |
1675
|
|
|
// $window_size == 3 if $window_ranges is between 25 and 81, for example. |
1676
|
|
|
for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) { |
|
|
|
|
1677
|
|
|
} |
1678
|
|
|
|
1679
|
|
|
$n_value = $n->value; |
1680
|
|
|
|
1681
|
|
|
// precompute $this^0 through $this^$window_size |
1682
|
|
|
$powers = []; |
1683
|
|
|
$powers[1] = self::_prepareReduce($this->value, $n_value, $mode); |
1684
|
|
|
$powers[2] = self::_squareReduce($powers[1], $n_value, $mode); |
1685
|
|
|
|
1686
|
|
|
// we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end |
1687
|
|
|
// in a 1. ie. it's supposed to be odd. |
1688
|
|
|
$temp = 1 << ($window_size - 1); |
1689
|
|
|
for ($i = 1; $i < $temp; ++$i) { |
1690
|
|
|
$i2 = $i << 1; |
1691
|
|
|
$powers[$i2 + 1] = self::_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); |
|
|
|
|
1692
|
|
|
} |
1693
|
|
|
|
1694
|
|
|
$result = [1]; |
1695
|
|
|
$result = self::_prepareReduce($result, $n_value, $mode); |
1696
|
|
|
|
1697
|
|
|
for ($i = 0; $i < $e_length;) { |
1698
|
|
|
if (!$e_bits[$i]) { |
1699
|
|
|
$result = self::_squareReduce($result, $n_value, $mode); |
|
|
|
|
1700
|
|
|
++$i; |
1701
|
|
|
} else { |
1702
|
|
|
for ($j = $window_size - 1; $j > 0; --$j) { |
1703
|
|
|
if (!empty($e_bits[$i + $j])) { |
1704
|
|
|
break; |
1705
|
|
|
} |
1706
|
|
|
} |
1707
|
|
|
|
1708
|
|
|
// eg. the length of substr($e_bits, $i, $j + 1) |
1709
|
|
|
for ($k = 0; $k <= $j; ++$k) { |
1710
|
|
|
$result = self::_squareReduce($result, $n_value, $mode); |
|
|
|
|
1711
|
|
|
} |
1712
|
|
|
|
1713
|
|
|
$result = self::_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); |
|
|
|
|
1714
|
|
|
|
1715
|
|
|
$i += $j + 1; |
1716
|
|
|
} |
1717
|
|
|
} |
1718
|
|
|
|
1719
|
|
|
$temp = new static(); |
1720
|
|
|
$temp->value = self::_reduce($result, $n_value, $mode); |
|
|
|
|
1721
|
|
|
|
1722
|
|
|
return $temp; |
1723
|
|
|
} |
1724
|
|
|
|
1725
|
|
|
/** |
1726
|
|
|
* Modular reduction. |
1727
|
|
|
* |
1728
|
|
|
* For most $modes this will return the remainder. |
1729
|
|
|
* |
1730
|
|
|
* @param array $x |
1731
|
|
|
* @param array $n |
1732
|
|
|
* @param int $mode |
1733
|
|
|
* |
1734
|
|
|
* @return array |
1735
|
|
|
*/ |
1736
|
|
|
private static function _reduce($x, $n, $mode) |
1737
|
|
|
{ |
1738
|
|
|
switch ($mode) { |
1739
|
|
|
case self::MONTGOMERY: |
1740
|
|
|
return self::_montgomery($x, $n); |
|
|
|
|
1741
|
|
|
case self::BARRETT: |
1742
|
|
|
return self::_barrett($x, $n); |
|
|
|
|
1743
|
|
|
case self::POWEROF2: |
1744
|
|
|
$lhs = new static(); |
1745
|
|
|
$lhs->value = $x; |
1746
|
|
|
$rhs = new static(); |
1747
|
|
|
$rhs->value = $n; |
1748
|
|
|
|
1749
|
|
|
return $x->_mod2($n); |
|
|
|
|
1750
|
|
|
case self::CLASSIC: |
1751
|
|
|
$lhs = new static(); |
1752
|
|
|
$lhs->value = $x; |
1753
|
|
|
$rhs = new static(); |
1754
|
|
|
$rhs->value = $n; |
1755
|
|
|
list(, $temp) = $lhs->divide($rhs); |
1756
|
|
|
|
1757
|
|
|
return $temp->value; |
1758
|
|
|
case self::NONE: |
1759
|
|
|
return $x; |
1760
|
|
|
default: |
1761
|
|
|
// an invalid $mode was provided |
1762
|
|
|
} |
1763
|
|
|
} |
1764
|
|
|
|
1765
|
|
|
/** |
1766
|
|
|
* Modular reduction preperation. |
1767
|
|
|
* |
1768
|
|
|
* @param array $x |
1769
|
|
|
* @param array $n |
1770
|
|
|
* @param int $mode |
1771
|
|
|
* |
1772
|
|
|
* @return array |
1773
|
|
|
*/ |
1774
|
|
|
private static function _prepareReduce($x, $n, $mode) |
1775
|
|
|
{ |
1776
|
|
|
if ($mode == self::MONTGOMERY) { |
1777
|
|
|
return self::_prepMontgomery($x, $n); |
1778
|
|
|
} |
1779
|
|
|
|
1780
|
|
|
return self::_reduce($x, $n, $mode); |
1781
|
|
|
} |
1782
|
|
|
|
1783
|
|
|
/** |
1784
|
|
|
* Modular multiply. |
1785
|
|
|
* |
1786
|
|
|
* @param array $x |
1787
|
|
|
* @param array $y |
1788
|
|
|
* @param array $n |
1789
|
|
|
* @param int $mode |
1790
|
|
|
* |
1791
|
|
|
* @return array |
1792
|
|
|
*/ |
1793
|
|
|
private static function _multiplyReduce($x, $y, $n, $mode) |
1794
|
|
|
{ |
1795
|
|
|
if ($mode == self::MONTGOMERY) { |
1796
|
|
|
return self::_montgomeryMultiply($x, $y, $n); |
1797
|
|
|
} |
1798
|
|
|
$temp = self::_multiply($x, false, $y, false); |
1799
|
|
|
|
1800
|
|
|
return self::_reduce($temp[self::VALUE], $n, $mode); |
|
|
|
|
1801
|
|
|
} |
1802
|
|
|
|
1803
|
|
|
/** |
1804
|
|
|
* Modular square. |
1805
|
|
|
* |
1806
|
|
|
* @param array $x |
1807
|
|
|
* @param array $n |
1808
|
|
|
* @param int $mode |
1809
|
|
|
* |
1810
|
|
|
* @return array |
1811
|
|
|
*/ |
1812
|
|
|
private static function _squareReduce($x, $n, $mode) |
1813
|
|
|
{ |
1814
|
|
|
if ($mode == self::MONTGOMERY) { |
1815
|
|
|
return self::_montgomeryMultiply($x, $x, $n); |
1816
|
|
|
} |
1817
|
|
|
|
1818
|
|
|
return self::_reduce(self::_square($x), $n, $mode); |
1819
|
|
|
} |
1820
|
|
|
|
1821
|
|
|
/** |
1822
|
|
|
* Modulos for Powers of Two. |
1823
|
|
|
* |
1824
|
|
|
* Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1), |
1825
|
|
|
* we'll just use this function as a wrapper for doing that. |
1826
|
|
|
* |
1827
|
|
|
* @param \Jose\Util\BigInteger |
1828
|
|
|
* |
1829
|
|
|
* @return \Jose\Util\BigInteger |
1830
|
|
|
*/ |
1831
|
|
|
private function _mod2($n) |
|
|
|
|
1832
|
|
|
{ |
1833
|
|
|
$temp = new static(); |
1834
|
|
|
$temp->value = [1]; |
1835
|
|
|
|
1836
|
|
|
return $this->bitwise_and($n->subtract($temp)); |
1837
|
|
|
} |
1838
|
|
|
|
1839
|
|
|
/** |
1840
|
|
|
* Barrett Modular Reduction. |
1841
|
|
|
* |
1842
|
|
|
* See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} / |
1843
|
|
|
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly, |
1844
|
|
|
* so as not to require negative numbers (initially, this script didn't support negative numbers). |
1845
|
|
|
* |
1846
|
|
|
* Employs "folding", as described at |
1847
|
|
|
* {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from |
1848
|
|
|
* it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x." |
1849
|
|
|
* |
1850
|
|
|
* Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that |
1851
|
|
|
* usable on account of (1) its not using reasonable radix points as discussed in |
1852
|
|
|
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable |
1853
|
|
|
* radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that |
1854
|
|
|
* (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line |
1855
|
|
|
* comments for details. |
1856
|
|
|
* |
1857
|
|
|
* @param array $n |
1858
|
|
|
* @param array $m |
1859
|
|
|
* |
1860
|
|
|
* @return array |
1861
|
|
|
*/ |
1862
|
|
|
private static function _barrett($n, $m) |
1863
|
|
|
{ |
1864
|
|
|
static $cache = [ |
1865
|
|
|
self::VARIABLE => [], |
1866
|
|
|
self::DATA => [], |
1867
|
|
|
]; |
1868
|
|
|
|
1869
|
|
|
$m_length = count($m); |
1870
|
|
|
|
1871
|
|
|
// if (self::_compare($n, self::_square($m)) >= 0) { |
1872
|
|
|
if (count($n) > 2 * $m_length) { |
1873
|
|
|
$lhs = new static(); |
1874
|
|
|
$rhs = new static(); |
1875
|
|
|
$lhs->value = $n; |
1876
|
|
|
$rhs->value = $m; |
1877
|
|
|
list(, $temp) = $lhs->divide($rhs); |
1878
|
|
|
|
1879
|
|
|
return $temp->value; |
1880
|
|
|
} |
1881
|
|
|
|
1882
|
|
|
// if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced |
1883
|
|
|
if ($m_length < 5) { |
1884
|
|
|
return self::_regularBarrett($n, $m); |
1885
|
|
|
} |
1886
|
|
|
|
1887
|
|
|
// n = 2 * m.length |
1888
|
|
|
|
1889
|
|
|
if (($key = array_search($m, $cache[self::VARIABLE])) === false) { |
1890
|
|
|
$key = count($cache[self::VARIABLE]); |
|
|
|
|
1891
|
|
|
$cache[self::VARIABLE][] = $m; |
1892
|
|
|
|
1893
|
|
|
$lhs = new static(); |
1894
|
|
|
$lhs_value = &$lhs->value; |
1895
|
|
|
$lhs_value = self::_array_repeat(0, $m_length + ($m_length >> 1)); |
1896
|
|
|
$lhs_value[] = 1; |
1897
|
|
|
$rhs = new static(); |
1898
|
|
|
$rhs->value = $m; |
1899
|
|
|
|
1900
|
|
|
list($u, $m1) = $lhs->divide($rhs); |
1901
|
|
|
$u = $u->value; |
1902
|
|
|
$m1 = $m1->value; |
1903
|
|
|
|
1904
|
|
|
$cache[self::DATA][] = [ |
1905
|
|
|
'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1) |
1906
|
|
|
'm1' => $m1, // m.length |
1907
|
|
|
]; |
1908
|
|
|
} else { |
1909
|
|
|
extract($cache[self::DATA][$key]); |
1910
|
|
|
} |
1911
|
|
|
|
1912
|
|
|
$cutoff = $m_length + ($m_length >> 1); |
1913
|
|
|
$lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1) |
1914
|
|
|
$msd = array_slice($n, $cutoff); // m.length >> 1 |
1915
|
|
|
$lsd = self::_trim($lsd); |
1916
|
|
|
$temp = self::_multiply($msd, false, $m1, false); |
1917
|
|
|
$n = self::_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 |
|
|
|
|
1918
|
|
|
|
1919
|
|
|
if ($m_length & 1) { |
1920
|
|
|
return self::_regularBarrett($n[self::VALUE], $m); |
|
|
|
|
1921
|
|
|
} |
1922
|
|
|
|
1923
|
|
|
// (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 |
1924
|
|
|
$temp = array_slice($n[self::VALUE], $m_length - 1); |
1925
|
|
|
// if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 |
1926
|
|
|
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 |
1927
|
|
|
$temp = self::_multiply($temp, false, $u, false); |
1928
|
|
|
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 |
1929
|
|
|
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) |
1930
|
|
|
$temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1); |
1931
|
|
|
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 |
1932
|
|
|
// if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) |
1933
|
|
|
$temp = self::_multiply($temp, false, $m, false); |
1934
|
|
|
|
1935
|
|
|
// at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit |
1936
|
|
|
// number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop |
1937
|
|
|
// following this comment would loop a lot (hence our calling _regularBarrett() in that situation). |
1938
|
|
|
|
1939
|
|
|
$result = self::_subtract($n[self::VALUE], false, $temp[self::VALUE], false); |
|
|
|
|
1940
|
|
|
|
1941
|
|
|
while (self::_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) { |
|
|
|
|
1942
|
|
|
$result = self::_subtract($result[self::VALUE], $result[self::SIGN], $m, false); |
|
|
|
|
1943
|
|
|
} |
1944
|
|
|
|
1945
|
|
|
return $result[self::VALUE]; |
1946
|
|
|
} |
1947
|
|
|
|
1948
|
|
|
/** |
1949
|
|
|
* (Regular) Barrett Modular Reduction. |
1950
|
|
|
* |
1951
|
|
|
* For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this |
1952
|
|
|
* is that this function does not fold the denominator into a smaller form. |
1953
|
|
|
* |
1954
|
|
|
* @param array $x |
1955
|
|
|
* @param array $n |
1956
|
|
|
* |
1957
|
|
|
* @return array |
1958
|
|
|
*/ |
1959
|
|
|
private static function _regularBarrett($x, $n) |
1960
|
|
|
{ |
1961
|
|
|
static $cache = [ |
1962
|
|
|
self::VARIABLE => [], |
1963
|
|
|
self::DATA => [], |
1964
|
|
|
]; |
1965
|
|
|
|
1966
|
|
|
$n_length = count($n); |
1967
|
|
|
|
1968
|
|
|
if (count($x) > 2 * $n_length) { |
1969
|
|
|
$lhs = new static(); |
1970
|
|
|
$rhs = new static(); |
1971
|
|
|
$lhs->value = $x; |
1972
|
|
|
$rhs->value = $n; |
1973
|
|
|
list(, $temp) = $lhs->divide($rhs); |
1974
|
|
|
|
1975
|
|
|
return $temp->value; |
1976
|
|
|
} |
1977
|
|
|
|
1978
|
|
|
if (($key = array_search($n, $cache[self::VARIABLE])) === false) { |
1979
|
|
|
$key = count($cache[self::VARIABLE]); |
1980
|
|
|
$cache[self::VARIABLE][] = $n; |
1981
|
|
|
$lhs = new static(); |
1982
|
|
|
$lhs_value = &$lhs->value; |
1983
|
|
|
$lhs_value = self::_array_repeat(0, 2 * $n_length); |
1984
|
|
|
$lhs_value[] = 1; |
1985
|
|
|
$rhs = new static(); |
1986
|
|
|
$rhs->value = $n; |
1987
|
|
|
list($temp) = $lhs->divide($rhs); // m.length |
1988
|
|
|
$cache[self::DATA][] = $temp->value; |
1989
|
|
|
} |
1990
|
|
|
|
1991
|
|
|
// 2 * m.length - (m.length - 1) = m.length + 1 |
1992
|
|
|
$temp = array_slice($x, $n_length - 1); |
1993
|
|
|
// (m.length + 1) + m.length = 2 * m.length + 1 |
1994
|
|
|
$temp = self::_multiply($temp, false, $cache[self::DATA][$key], false); |
1995
|
|
|
// (2 * m.length + 1) - (m.length - 1) = m.length + 2 |
1996
|
|
|
$temp = array_slice($temp[self::VALUE], $n_length + 1); |
1997
|
|
|
|
1998
|
|
|
// m.length + 1 |
1999
|
|
|
$result = array_slice($x, 0, $n_length + 1); |
2000
|
|
|
// m.length + 1 |
2001
|
|
|
$temp = self::_multiplyLower($temp, false, $n, false, $n_length + 1); |
2002
|
|
|
// $temp == array_slice(self::_multiply($temp, false, $n, false)->value, 0, $n_length + 1) |
2003
|
|
|
|
2004
|
|
|
if (self::_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) { |
|
|
|
|
2005
|
|
|
$corrector_value = self::_array_repeat(0, $n_length + 1); |
2006
|
|
|
$corrector_value[count($corrector_value)] = 1; |
2007
|
|
|
$result = self::_add($result, false, $corrector_value, false); |
2008
|
|
|
$result = $result[self::VALUE]; |
2009
|
|
|
} |
2010
|
|
|
|
2011
|
|
|
// at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits |
2012
|
|
|
$result = self::_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]); |
|
|
|
|
2013
|
|
|
while (self::_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) { |
|
|
|
|
2014
|
|
|
$result = self::_subtract($result[self::VALUE], $result[self::SIGN], $n, false); |
|
|
|
|
2015
|
|
|
} |
2016
|
|
|
|
2017
|
|
|
return $result[self::VALUE]; |
2018
|
|
|
} |
2019
|
|
|
|
2020
|
|
|
/** |
2021
|
|
|
* Performs long multiplication up to $stop digits. |
2022
|
|
|
* |
2023
|
|
|
* If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved. |
2024
|
|
|
* |
2025
|
|
|
* @param array $x_value |
2026
|
|
|
* @param bool $x_negative |
2027
|
|
|
* @param array $y_value |
2028
|
|
|
* @param bool $y_negative |
2029
|
|
|
* @param int $stop |
2030
|
|
|
* |
2031
|
|
|
* @return array |
2032
|
|
|
*/ |
2033
|
|
|
private static function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) |
2034
|
|
|
{ |
2035
|
|
|
$x_length = count($x_value); |
2036
|
|
|
$y_length = count($y_value); |
2037
|
|
|
|
2038
|
|
|
if (!$x_length || !$y_length) { // a 0 is being multiplied |
2039
|
|
|
return [ |
2040
|
|
|
self::VALUE => [], |
2041
|
|
|
self::SIGN => false, |
2042
|
|
|
]; |
2043
|
|
|
} |
2044
|
|
|
|
2045
|
|
|
if ($x_length < $y_length) { |
2046
|
|
|
$temp = $x_value; |
2047
|
|
|
$x_value = $y_value; |
2048
|
|
|
$y_value = $temp; |
2049
|
|
|
|
2050
|
|
|
$x_length = count($x_value); |
2051
|
|
|
$y_length = count($y_value); |
2052
|
|
|
} |
2053
|
|
|
|
2054
|
|
|
$product_value = self::_array_repeat(0, $x_length + $y_length); |
2055
|
|
|
|
2056
|
|
|
// the following for loop could be removed if the for loop following it |
2057
|
|
|
// (the one with nested for loops) initially set $i to 0, but |
2058
|
|
|
// doing so would also make the result in one set of unnecessary adds, |
2059
|
|
|
// since on the outermost loops first pass, $product->value[$k] is going |
2060
|
|
|
// to always be 0 |
2061
|
|
|
|
2062
|
|
|
$carry = 0; |
2063
|
|
|
|
2064
|
|
|
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i |
2065
|
|
|
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 |
2066
|
|
|
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
2067
|
|
|
$product_value[$j] = (int) ($temp - self::$baseFull * $carry); |
2068
|
|
|
} |
2069
|
|
|
|
2070
|
|
|
if ($j < $stop) { |
2071
|
|
|
$product_value[$j] = $carry; |
2072
|
|
|
} |
2073
|
|
|
|
2074
|
|
|
// the above for loop is what the previous comment was talking about. the |
2075
|
|
|
// following for loop is the "one with nested for loops" |
2076
|
|
|
|
2077
|
|
|
for ($i = 1; $i < $y_length; ++$i) { |
2078
|
|
|
$carry = 0; |
2079
|
|
|
|
2080
|
|
|
for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { |
2081
|
|
|
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; |
2082
|
|
|
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
2083
|
|
|
$product_value[$k] = (int) ($temp - self::$baseFull * $carry); |
2084
|
|
|
} |
2085
|
|
|
|
2086
|
|
|
if ($k < $stop) { |
2087
|
|
|
$product_value[$k] = $carry; |
2088
|
|
|
} |
2089
|
|
|
} |
2090
|
|
|
|
2091
|
|
|
return [ |
2092
|
|
|
self::VALUE => self::_trim($product_value), |
2093
|
|
|
self::SIGN => $x_negative != $y_negative, |
2094
|
|
|
]; |
2095
|
|
|
} |
2096
|
|
|
|
2097
|
|
|
/** |
2098
|
|
|
* Montgomery Modular Reduction. |
2099
|
|
|
* |
2100
|
|
|
* ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n. |
2101
|
|
|
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be |
2102
|
|
|
* improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function |
2103
|
|
|
* to work correctly. |
2104
|
|
|
* |
2105
|
|
|
* @param array $x |
2106
|
|
|
* @param array $n |
2107
|
|
|
* |
2108
|
|
|
* @return array |
2109
|
|
|
*/ |
2110
|
|
|
private static function _montgomery($x, $n) |
2111
|
|
|
{ |
2112
|
|
|
static $cache = [ |
2113
|
|
|
self::VARIABLE => [], |
2114
|
|
|
self::DATA => [], |
2115
|
|
|
]; |
2116
|
|
|
|
2117
|
|
|
if (($key = array_search($n, $cache[self::VARIABLE])) === false) { |
2118
|
|
|
$key = count($cache[self::VARIABLE]); |
2119
|
|
|
$cache[self::VARIABLE][] = $x; |
2120
|
|
|
$cache[self::DATA][] = self::_modInverse67108864($n); |
2121
|
|
|
} |
2122
|
|
|
|
2123
|
|
|
$k = count($n); |
2124
|
|
|
|
2125
|
|
|
$result = [self::VALUE => $x]; |
2126
|
|
|
|
2127
|
|
|
for ($i = 0; $i < $k; ++$i) { |
2128
|
|
|
$temp = $result[self::VALUE][$i] * $cache[self::DATA][$key]; |
2129
|
|
|
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); |
2130
|
|
|
$temp = self::_regularMultiply([$temp], $n); |
2131
|
|
|
$temp = array_merge($this->_array_repeat(0, $i), $temp); |
|
|
|
|
2132
|
|
|
$result = self::_add($result[self::VALUE], false, $temp, false); |
|
|
|
|
2133
|
|
|
} |
2134
|
|
|
|
2135
|
|
|
$result[self::VALUE] = array_slice($result[self::VALUE], $k); |
2136
|
|
|
|
2137
|
|
|
if (self::_compare($result, false, $n, false) >= 0) { |
2138
|
|
|
$result = self::_subtract($result[self::VALUE], false, $n, false); |
|
|
|
|
2139
|
|
|
} |
2140
|
|
|
|
2141
|
|
|
return $result[self::VALUE]; |
2142
|
|
|
} |
2143
|
|
|
|
2144
|
|
|
/** |
2145
|
|
|
* Montgomery Multiply. |
2146
|
|
|
* |
2147
|
|
|
* Interleaves the montgomery reduction and long multiplication algorithms together as described in |
2148
|
|
|
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36} |
2149
|
|
|
* |
2150
|
|
|
* @param array $x |
2151
|
|
|
* @param array $y |
2152
|
|
|
* @param array $m |
2153
|
|
|
* |
2154
|
|
|
* @return array |
2155
|
|
|
*/ |
2156
|
|
|
private static function _montgomeryMultiply($x, $y, $m) |
2157
|
|
|
{ |
2158
|
|
|
$temp = self::_multiply($x, false, $y, false); |
2159
|
|
|
|
2160
|
|
|
return self::_montgomery($temp[self::VALUE], $m); |
|
|
|
|
2161
|
|
|
|
2162
|
|
|
// the following code, although not callable, can be run independently of the above code |
2163
|
|
|
// although the above code performed better in my benchmarks the following could might |
2164
|
|
|
// perform better under different circumstances. in lieu of deleting it it's just been |
2165
|
|
|
// made uncallable |
2166
|
|
|
|
2167
|
|
|
static $cache = [ |
|
|
|
|
2168
|
|
|
self::VARIABLE => [], |
2169
|
|
|
self::DATA => [], |
2170
|
|
|
]; |
2171
|
|
|
|
2172
|
|
|
if (($key = array_search($m, $cache[self::VARIABLE])) === false) { |
2173
|
|
|
$key = count($cache[self::VARIABLE]); |
2174
|
|
|
$cache[self::VARIABLE][] = $m; |
2175
|
|
|
$cache[self::DATA][] = self::_modInverse67108864($m); |
2176
|
|
|
} |
2177
|
|
|
|
2178
|
|
|
$n = max(count($x), count($y), count($m)); |
2179
|
|
|
$x = array_pad($x, $n, 0); |
2180
|
|
|
$y = array_pad($y, $n, 0); |
2181
|
|
|
$m = array_pad($m, $n, 0); |
2182
|
|
|
$a = [self::VALUE => self::_array_repeat(0, $n + 1)]; |
2183
|
|
|
for ($i = 0; $i < $n; ++$i) { |
2184
|
|
|
$temp = $a[self::VALUE][0] + $x[$i] * $y[0]; |
2185
|
|
|
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); |
2186
|
|
|
$temp = $temp * $cache[self::DATA][$key]; |
2187
|
|
|
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); |
2188
|
|
|
$temp = self::_add(self::_regularMultiply([$x[$i]], $y), false, self::_regularMultiply([$temp], $m), false); |
2189
|
|
|
$a = self::_add($a[self::VALUE], false, $temp[self::VALUE], false); |
|
|
|
|
2190
|
|
|
$a[self::VALUE] = array_slice($a[self::VALUE], 1); |
2191
|
|
|
} |
2192
|
|
|
if (self::_compare($a[self::VALUE], false, $m, false) >= 0) { |
|
|
|
|
2193
|
|
|
$a = self::_subtract($a[self::VALUE], false, $m, false); |
|
|
|
|
2194
|
|
|
} |
2195
|
|
|
|
2196
|
|
|
return $a[self::VALUE]; |
2197
|
|
|
} |
2198
|
|
|
|
2199
|
|
|
/** |
2200
|
|
|
* Prepare a number for use in Montgomery Modular Reductions. |
2201
|
|
|
* |
2202
|
|
|
* @param array $x |
2203
|
|
|
* @param array $n |
2204
|
|
|
* |
2205
|
|
|
* @return array |
2206
|
|
|
*/ |
2207
|
|
|
private static function _prepMontgomery($x, $n) |
2208
|
|
|
{ |
2209
|
|
|
$lhs = new static(); |
2210
|
|
|
$lhs->value = array_merge(self::_array_repeat(0, count($n)), $x); |
2211
|
|
|
$rhs = new static(); |
2212
|
|
|
$rhs->value = $n; |
2213
|
|
|
|
2214
|
|
|
list(, $temp) = $lhs->divide($rhs); |
2215
|
|
|
|
2216
|
|
|
return $temp->value; |
2217
|
|
|
} |
2218
|
|
|
|
2219
|
|
|
/** |
2220
|
|
|
* Modular Inverse of a number mod 2**26 (eg. 67108864). |
2221
|
|
|
* |
2222
|
|
|
* Based off of the bnpInvDigit function implemented and justified in the following URL: |
2223
|
|
|
* |
2224
|
|
|
* {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js} |
2225
|
|
|
* |
2226
|
|
|
* The following URL provides more info: |
2227
|
|
|
* |
2228
|
|
|
* {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85} |
2229
|
|
|
* |
2230
|
|
|
* As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For |
2231
|
|
|
* instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields |
2232
|
|
|
* int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't |
2233
|
|
|
* auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that |
2234
|
|
|
* the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the |
2235
|
|
|
* maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to |
2236
|
|
|
* 40 bits, which only 64-bit floating points will support. |
2237
|
|
|
* |
2238
|
|
|
* Thanks to Pedro Gimeno Fortea for input! |
2239
|
|
|
* |
2240
|
|
|
* @param array $x |
2241
|
|
|
* |
2242
|
|
|
* @return int |
2243
|
|
|
*/ |
2244
|
|
|
private function _modInverse67108864($x) // 2**26 == 67,108,864 |
2245
|
|
|
{ |
2246
|
|
|
$x = -$x[0]; |
2247
|
|
|
$result = $x & 0x3; // x**-1 mod 2**2 |
2248
|
|
|
$result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4 |
2249
|
|
|
$result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8 |
2250
|
|
|
$result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16 |
2251
|
|
|
$result = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); // x**-1 mod 2**26 |
2252
|
|
|
return $result & self::$maxDigit; |
2253
|
|
|
} |
2254
|
|
|
|
2255
|
|
|
/** |
2256
|
|
|
* Calculates modular inverses. |
2257
|
|
|
* |
2258
|
|
|
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. |
2259
|
|
|
* |
2260
|
|
|
* Here's an example: |
2261
|
|
|
* <code> |
2262
|
|
|
* <?php |
2263
|
|
|
* $a = new \Jose\Util\teger(30); |
2264
|
|
|
* $b = new \Jose\Util\teger(17); |
2265
|
|
|
* |
2266
|
|
|
* $c = $a->modInverse($b); |
2267
|
|
|
* echo $c->toString(); // outputs 4 |
2268
|
|
|
* |
2269
|
|
|
* echo "\r\n"; |
2270
|
|
|
* |
2271
|
|
|
* $d = $a->multiply($c); |
2272
|
|
|
* list(, $d) = $d->divide($b); |
2273
|
|
|
* echo $d; // outputs 1 (as per the definition of modular inverse) |
2274
|
|
|
* ?> |
2275
|
|
|
* </code> |
2276
|
|
|
* |
2277
|
|
|
* @param \Jose\Util\Integer $n |
2278
|
|
|
* |
2279
|
|
|
* @return \Jose\Util\eger|false |
2280
|
|
|
* |
2281
|
|
|
* @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information. |
2282
|
|
|
*/ |
2283
|
|
|
public function modInverse(BigInteger $n) |
2284
|
|
|
{ |
2285
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
2286
|
|
|
case self::MODE_GMP: |
2287
|
|
|
$temp = new static(); |
2288
|
|
|
$temp->value = gmp_invert($this->value, $n->value); |
|
|
|
|
2289
|
|
|
|
2290
|
|
|
return ($temp->value === false) ? false : $this->_normalize($temp); |
2291
|
|
|
} |
2292
|
|
|
|
2293
|
|
|
static $zero, $one; |
2294
|
|
|
if (!isset($zero)) { |
2295
|
|
|
$zero = new static(); |
2296
|
|
|
$one = new static(1); |
2297
|
|
|
} |
2298
|
|
|
|
2299
|
|
|
// $x mod -$n == $x mod $n. |
2300
|
|
|
$n = $n->abs(); |
2301
|
|
|
|
2302
|
|
|
if ($this->compare($zero) < 0) { |
2303
|
|
|
$temp = $this->abs(); |
2304
|
|
|
$temp = $temp->modInverse($n); |
2305
|
|
|
|
2306
|
|
|
return $this->_normalize($n->subtract($temp)); |
|
|
|
|
2307
|
|
|
} |
2308
|
|
|
|
2309
|
|
|
extract($this->extendedGCD($n)); |
|
|
|
|
2310
|
|
|
|
2311
|
|
|
if (!$gcd->equals($one)) { |
2312
|
|
|
return false; |
2313
|
|
|
} |
2314
|
|
|
|
2315
|
|
|
$x = $x->compare($zero) < 0 ? $x->add($n) : $x; |
2316
|
|
|
|
2317
|
|
|
return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x); |
2318
|
|
|
} |
2319
|
|
|
|
2320
|
|
|
/** |
2321
|
|
|
* Calculates the greatest common divisor and Bezout's identity. |
2322
|
|
|
* |
2323
|
|
|
* Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that |
2324
|
|
|
* 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which |
2325
|
|
|
* combination is returned is dependant upon which mode is in use. See |
2326
|
|
|
* {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. |
2327
|
|
|
* |
2328
|
|
|
* Here's an example: |
2329
|
|
|
* <code> |
2330
|
|
|
* <?php |
2331
|
|
|
* $a = new \Jose\Util\eger(693); |
2332
|
|
|
* $b = new \Jose\Util\eger(609); |
2333
|
|
|
* |
2334
|
|
|
* extract($a->extendedGCD($b)); |
2335
|
|
|
* |
2336
|
|
|
* echo $gcd->toString() . "\r\n"; // outputs 21 |
2337
|
|
|
* echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21 |
2338
|
|
|
* ?> |
2339
|
|
|
* </code> |
2340
|
|
|
* |
2341
|
|
|
* @param \Jose\Util\Integer $n |
2342
|
|
|
* |
2343
|
|
|
* @return \Jose\Util\BigInteger |
2344
|
|
|
* |
2345
|
|
|
* @internal Calculates the GCD using the binary xGCD algorithim described in |
2346
|
|
|
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes, |
2347
|
|
|
* the more traditional algorithim requires "relatively costly multiple-precision divisions". |
2348
|
|
|
*/ |
2349
|
|
|
public function extendedGCD(BigInteger $n) |
2350
|
|
|
{ |
2351
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
2352
|
|
|
case self::MODE_GMP: |
2353
|
|
|
extract(gmp_gcdext($this->value, $n->value)); |
|
|
|
|
2354
|
|
|
|
2355
|
|
|
return [ |
2356
|
|
|
'gcd' => $this->_normalize(new static($g)), |
2357
|
|
|
'x' => $this->_normalize(new static($s)), |
2358
|
|
|
'y' => $this->_normalize(new static($t)), |
2359
|
|
|
]; |
2360
|
|
|
case self::MODE_BCMATH: |
2361
|
|
|
// it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works |
2362
|
|
|
// best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is, |
2363
|
|
|
// the basic extended euclidean algorithim is what we're using. |
2364
|
|
|
|
2365
|
|
|
$u = $this->value; |
2366
|
|
|
$v = $n->value; |
2367
|
|
|
|
2368
|
|
|
$a = '1'; |
2369
|
|
|
$b = '0'; |
2370
|
|
|
$c = '0'; |
2371
|
|
|
$d = '1'; |
2372
|
|
|
|
2373
|
|
|
while (bccomp($v, '0', 0) != 0) { |
2374
|
|
|
$q = bcdiv($u, $v, 0); |
2375
|
|
|
|
2376
|
|
|
$temp = $u; |
2377
|
|
|
$u = $v; |
2378
|
|
|
$v = bcsub($temp, bcmul($v, $q, 0), 0); |
2379
|
|
|
|
2380
|
|
|
$temp = $a; |
2381
|
|
|
$a = $c; |
2382
|
|
|
$c = bcsub($temp, bcmul($a, $q, 0), 0); |
2383
|
|
|
|
2384
|
|
|
$temp = $b; |
2385
|
|
|
$b = $d; |
2386
|
|
|
$d = bcsub($temp, bcmul($b, $q, 0), 0); |
2387
|
|
|
} |
2388
|
|
|
|
2389
|
|
|
return [ |
2390
|
|
|
'gcd' => $this->_normalize(new static($u)), |
|
|
|
|
2391
|
|
|
'x' => $this->_normalize(new static($a)), |
2392
|
|
|
'y' => $this->_normalize(new static($b)), |
2393
|
|
|
]; |
2394
|
|
|
} |
2395
|
|
|
|
2396
|
|
|
$y = clone $n; |
2397
|
|
|
$x = clone $this; |
2398
|
|
|
$g = new static(); |
2399
|
|
|
$g->value = [1]; |
2400
|
|
|
|
2401
|
|
|
while (!(($x->value[0] & 1) || ($y->value[0] & 1))) { |
2402
|
|
|
$x->_rshift(1); |
2403
|
|
|
$y->_rshift(1); |
2404
|
|
|
$g->_lshift(1); |
2405
|
|
|
} |
2406
|
|
|
|
2407
|
|
|
$u = clone $x; |
2408
|
|
|
$v = clone $y; |
2409
|
|
|
|
2410
|
|
|
$a = new static(); |
2411
|
|
|
$b = new static(); |
2412
|
|
|
$c = new static(); |
2413
|
|
|
$d = new static(); |
2414
|
|
|
|
2415
|
|
|
$a->value = $d->value = $g->value = [1]; |
2416
|
|
|
$b->value = $c->value = []; |
2417
|
|
|
|
2418
|
|
|
while (!empty($u->value)) { |
2419
|
|
|
while (!($u->value[0] & 1)) { |
2420
|
|
|
$u->_rshift(1); |
2421
|
|
|
if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) { |
2422
|
|
|
$a = $a->add($y); |
2423
|
|
|
$b = $b->subtract($x); |
2424
|
|
|
} |
2425
|
|
|
$a->_rshift(1); |
2426
|
|
|
$b->_rshift(1); |
2427
|
|
|
} |
2428
|
|
|
|
2429
|
|
|
while (!($v->value[0] & 1)) { |
2430
|
|
|
$v->_rshift(1); |
2431
|
|
|
if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) { |
2432
|
|
|
$c = $c->add($y); |
2433
|
|
|
$d = $d->subtract($x); |
2434
|
|
|
} |
2435
|
|
|
$c->_rshift(1); |
2436
|
|
|
$d->_rshift(1); |
2437
|
|
|
} |
2438
|
|
|
|
2439
|
|
|
if ($u->compare($v) >= 0) { |
2440
|
|
|
$u = $u->subtract($v); |
2441
|
|
|
$a = $a->subtract($c); |
2442
|
|
|
$b = $b->subtract($d); |
2443
|
|
|
} else { |
2444
|
|
|
$v = $v->subtract($u); |
2445
|
|
|
$c = $c->subtract($a); |
2446
|
|
|
$d = $d->subtract($b); |
2447
|
|
|
} |
2448
|
|
|
} |
2449
|
|
|
|
2450
|
|
|
return [ |
2451
|
|
|
'gcd' => $this->_normalize($g->multiply($v)), |
2452
|
|
|
'x' => $this->_normalize($c), |
2453
|
|
|
'y' => $this->_normalize($d), |
2454
|
|
|
]; |
2455
|
|
|
} |
2456
|
|
|
|
2457
|
|
|
/** |
2458
|
|
|
* Calculates the greatest common divisor. |
2459
|
|
|
* |
2460
|
|
|
* Say you have 693 and 609. The GCD is 21. |
2461
|
|
|
* |
2462
|
|
|
* Here's an example: |
2463
|
|
|
* <code> |
2464
|
|
|
* <?php |
2465
|
|
|
* $a = new \Jose\Util\eger(693); |
2466
|
|
|
* $b = new \Jose\Util\eger(609); |
2467
|
|
|
* |
2468
|
|
|
* $gcd = a->extendedGCD($b); |
2469
|
|
|
* |
2470
|
|
|
* echo $gcd->toString() . "\r\n"; // outputs 21 |
2471
|
|
|
* ?> |
2472
|
|
|
* </code> |
2473
|
|
|
* |
2474
|
|
|
* @param \Jose\Util\Integer $n |
2475
|
|
|
* |
2476
|
|
|
* @return \Jose\Util\BigInteger |
2477
|
|
|
*/ |
2478
|
|
|
public function gcd(BigInteger $n) |
2479
|
|
|
{ |
2480
|
|
|
extract($this->extendedGCD($n)); |
|
|
|
|
2481
|
|
|
|
2482
|
|
|
return $gcd; |
2483
|
|
|
} |
2484
|
|
|
|
2485
|
|
|
/** |
2486
|
|
|
* Absolute value. |
2487
|
|
|
* |
2488
|
|
|
* @return \Jose\Util\BigInteger |
2489
|
|
|
*/ |
2490
|
|
|
public function abs() |
2491
|
|
|
{ |
2492
|
|
|
$temp = new static(); |
2493
|
|
|
|
2494
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
2495
|
|
|
case self::MODE_GMP: |
2496
|
|
|
$temp->value = gmp_abs($this->value); |
|
|
|
|
2497
|
|
|
break; |
2498
|
|
|
case self::MODE_BCMATH: |
2499
|
|
|
$temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value; |
|
|
|
|
2500
|
|
|
break; |
2501
|
|
|
default: |
2502
|
|
|
$temp->value = $this->value; |
2503
|
|
|
} |
2504
|
|
|
|
2505
|
|
|
return $temp; |
2506
|
|
|
} |
2507
|
|
|
|
2508
|
|
|
/** |
2509
|
|
|
* Compares two numbers. |
2510
|
|
|
* |
2511
|
|
|
* Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is |
2512
|
|
|
* demonstrated thusly: |
2513
|
|
|
* |
2514
|
|
|
* $x > $y: $x->compare($y) > 0 |
2515
|
|
|
* $x < $y: $x->compare($y) < 0 |
2516
|
|
|
* $x == $y: $x->compare($y) == 0 |
2517
|
|
|
* |
2518
|
|
|
* Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). |
2519
|
|
|
* |
2520
|
|
|
* @param \Jose\Util\Integer $y |
2521
|
|
|
* |
2522
|
|
|
* @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. |
2523
|
|
|
* |
2524
|
|
|
* @internal Could return $this->subtract($x), but that's not as fast as what we do do. |
2525
|
|
|
*/ |
2526
|
|
|
public function compare(BigInteger $y) |
2527
|
|
|
{ |
2528
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
2529
|
|
|
case self::MODE_GMP: |
2530
|
|
|
return gmp_cmp($this->value, $y->value); |
2531
|
|
|
case self::MODE_BCMATH: |
2532
|
|
|
return bccomp($this->value, $y->value, 0); |
2533
|
|
|
} |
2534
|
|
|
|
2535
|
|
|
return self::_compare($this->value, $this->is_negative, $y->value, $y->is_negative); |
2536
|
|
|
} |
2537
|
|
|
|
2538
|
|
|
/** |
2539
|
|
|
* Compares two numbers. |
2540
|
|
|
* |
2541
|
|
|
* @param array $x_value |
2542
|
|
|
* @param bool $x_negative |
2543
|
|
|
* @param array $y_value |
2544
|
|
|
* @param bool $y_negative |
2545
|
|
|
* |
2546
|
|
|
* @return int |
2547
|
|
|
*/ |
2548
|
|
|
private static function _compare($x_value, $x_negative, $y_value, $y_negative) |
2549
|
|
|
{ |
2550
|
|
|
if ($x_negative != $y_negative) { |
2551
|
|
|
return (!$x_negative && $y_negative) ? 1 : -1; |
2552
|
|
|
} |
2553
|
|
|
|
2554
|
|
|
$result = $x_negative ? -1 : 1; |
2555
|
|
|
|
2556
|
|
|
if (count($x_value) != count($y_value)) { |
2557
|
|
|
return (count($x_value) > count($y_value)) ? $result : -$result; |
2558
|
|
|
} |
2559
|
|
|
$size = max(count($x_value), count($y_value)); |
2560
|
|
|
|
2561
|
|
|
$x_value = array_pad($x_value, $size, 0); |
2562
|
|
|
$y_value = array_pad($y_value, $size, 0); |
2563
|
|
|
|
2564
|
|
|
for ($i = count($x_value) - 1; $i >= 0; --$i) { |
2565
|
|
|
if ($x_value[$i] != $y_value[$i]) { |
2566
|
|
|
return ($x_value[$i] > $y_value[$i]) ? $result : -$result; |
2567
|
|
|
} |
2568
|
|
|
} |
2569
|
|
|
|
2570
|
|
|
return 0; |
2571
|
|
|
} |
2572
|
|
|
|
2573
|
|
|
/** |
2574
|
|
|
* Tests the equality of two numbers. |
2575
|
|
|
* |
2576
|
|
|
* If you need to see if one number is greater than or less than another number, use BigInteger::compare() |
2577
|
|
|
* |
2578
|
|
|
* @param \Jose\Util\Integer $x |
2579
|
|
|
* |
2580
|
|
|
* @return bool |
2581
|
|
|
*/ |
2582
|
|
|
public function equals(BigInteger $x) |
2583
|
|
|
{ |
2584
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
2585
|
|
|
case self::MODE_GMP: |
2586
|
|
|
return gmp_cmp($this->value, $x->value) == 0; |
2587
|
|
|
default: |
2588
|
|
|
return $this->value === $x->value && $this->is_negative == $x->is_negative; |
2589
|
|
|
} |
2590
|
|
|
} |
2591
|
|
|
|
2592
|
|
|
/** |
2593
|
|
|
* Set Precision. |
2594
|
|
|
* |
2595
|
|
|
* Some bitwise operations give different results depending on the precision being used. Examples include left |
2596
|
|
|
* shift, not, and rotates. |
2597
|
|
|
* |
2598
|
|
|
* @param int $bits |
2599
|
|
|
*/ |
2600
|
|
|
public function setPrecision($bits) |
2601
|
|
|
{ |
2602
|
|
|
if ($bits < 1) { |
2603
|
|
|
$this->precision = -1; |
2604
|
|
|
$this->bitmask = false; |
2605
|
|
|
|
2606
|
|
|
return; |
2607
|
|
|
} |
2608
|
|
|
$this->precision = $bits; |
2609
|
|
|
if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) { |
2610
|
|
|
$this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1).str_repeat(chr(0xFF), $bits >> 3), 256); |
|
|
|
|
2611
|
|
|
} else { |
2612
|
|
|
$this->bitmask = new static(bcpow('2', $bits, 0)); |
|
|
|
|
2613
|
|
|
} |
2614
|
|
|
|
2615
|
|
|
$temp = $this->_normalize($this); |
2616
|
|
|
$this->value = $temp->value; |
2617
|
|
|
} |
2618
|
|
|
|
2619
|
|
|
/** |
2620
|
|
|
* Get Precision. |
2621
|
|
|
* |
2622
|
|
|
* @return int |
2623
|
|
|
*/ |
2624
|
|
|
public function getPrecision() |
2625
|
|
|
{ |
2626
|
|
|
return $this->precision; |
2627
|
|
|
} |
2628
|
|
|
|
2629
|
|
|
/** |
2630
|
|
|
* Logical And. |
2631
|
|
|
* |
2632
|
|
|
* @param \Jose\Util\Integer $x |
2633
|
|
|
* |
2634
|
|
|
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat> |
2635
|
|
|
* |
2636
|
|
|
* @return \Jose\Util\BigInteger |
2637
|
|
|
*/ |
2638
|
|
|
public function bitwise_and(BigInteger $x) |
2639
|
|
|
{ |
2640
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
2641
|
|
|
case self::MODE_GMP: |
2642
|
|
|
$temp = new static(); |
2643
|
|
|
$temp->value = gmp_and($this->value, $x->value); |
|
|
|
|
2644
|
|
|
|
2645
|
|
|
return $this->_normalize($temp); |
2646
|
|
|
case self::MODE_BCMATH: |
2647
|
|
|
$left = $this->toBytes(); |
2648
|
|
|
$right = $x->toBytes(); |
2649
|
|
|
|
2650
|
|
|
$length = max(strlen($left), strlen($right)); |
2651
|
|
|
|
2652
|
|
|
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT); |
2653
|
|
|
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT); |
2654
|
|
|
|
2655
|
|
|
return $this->_normalize(new static($left & $right, 256)); |
2656
|
|
|
} |
2657
|
|
|
|
2658
|
|
|
$result = clone $this; |
2659
|
|
|
|
2660
|
|
|
$length = min(count($x->value), count($this->value)); |
2661
|
|
|
|
2662
|
|
|
$result->value = array_slice($result->value, 0, $length); |
2663
|
|
|
|
2664
|
|
|
for ($i = 0; $i < $length; ++$i) { |
2665
|
|
|
$result->value[$i] &= $x->value[$i]; |
2666
|
|
|
} |
2667
|
|
|
|
2668
|
|
|
return $this->_normalize($result); |
2669
|
|
|
} |
2670
|
|
|
|
2671
|
|
|
/** |
2672
|
|
|
* Logical Or. |
2673
|
|
|
* |
2674
|
|
|
* @param \Jose\Util\Integer $x |
2675
|
|
|
* |
2676
|
|
|
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat> |
2677
|
|
|
* |
2678
|
|
|
* @return \Jose\Util\BigInteger |
2679
|
|
|
*/ |
2680
|
|
|
public function bitwise_or(BigInteger $x) |
2681
|
|
|
{ |
2682
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
2683
|
|
|
case self::MODE_GMP: |
2684
|
|
|
$temp = new static(); |
2685
|
|
|
$temp->value = gmp_or($this->value, $x->value); |
|
|
|
|
2686
|
|
|
|
2687
|
|
|
return $this->_normalize($temp); |
2688
|
|
|
case self::MODE_BCMATH: |
2689
|
|
|
$left = $this->toBytes(); |
2690
|
|
|
$right = $x->toBytes(); |
2691
|
|
|
|
2692
|
|
|
$length = max(strlen($left), strlen($right)); |
2693
|
|
|
|
2694
|
|
|
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT); |
2695
|
|
|
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT); |
2696
|
|
|
|
2697
|
|
|
return $this->_normalize(new static($left | $right, 256)); |
2698
|
|
|
} |
2699
|
|
|
|
2700
|
|
|
$length = max(count($this->value), count($x->value)); |
2701
|
|
|
$result = clone $this; |
2702
|
|
|
$result->value = array_pad($result->value, $length, 0); |
2703
|
|
|
$x->value = array_pad($x->value, $length, 0); |
2704
|
|
|
|
2705
|
|
|
for ($i = 0; $i < $length; ++$i) { |
2706
|
|
|
$result->value[$i] |= $x->value[$i]; |
2707
|
|
|
} |
2708
|
|
|
|
2709
|
|
|
return $this->_normalize($result); |
2710
|
|
|
} |
2711
|
|
|
|
2712
|
|
|
/** |
2713
|
|
|
* Logical Exclusive-Or. |
2714
|
|
|
* |
2715
|
|
|
* @param \Jose\Util\Integer $x |
2716
|
|
|
* |
2717
|
|
|
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat> |
2718
|
|
|
* |
2719
|
|
|
* @return \Jose\Util\BigInteger |
2720
|
|
|
*/ |
2721
|
|
|
public function bitwise_xor(BigInteger $x) |
2722
|
|
|
{ |
2723
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
2724
|
|
|
case self::MODE_GMP: |
2725
|
|
|
$temp = new static(); |
2726
|
|
|
$temp->value = gmp_xor($this->value, $x->value); |
|
|
|
|
2727
|
|
|
|
2728
|
|
|
return $this->_normalize($temp); |
2729
|
|
|
case self::MODE_BCMATH: |
2730
|
|
|
$left = $this->toBytes(); |
2731
|
|
|
$right = $x->toBytes(); |
2732
|
|
|
|
2733
|
|
|
$length = max(strlen($left), strlen($right)); |
2734
|
|
|
|
2735
|
|
|
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT); |
2736
|
|
|
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT); |
2737
|
|
|
|
2738
|
|
|
return $this->_normalize(new static($left ^ $right, 256)); |
2739
|
|
|
} |
2740
|
|
|
|
2741
|
|
|
$length = max(count($this->value), count($x->value)); |
2742
|
|
|
$result = clone $this; |
2743
|
|
|
$result->value = array_pad($result->value, $length, 0); |
2744
|
|
|
$x->value = array_pad($x->value, $length, 0); |
2745
|
|
|
|
2746
|
|
|
for ($i = 0; $i < $length; ++$i) { |
2747
|
|
|
$result->value[$i] ^= $x->value[$i]; |
2748
|
|
|
} |
2749
|
|
|
|
2750
|
|
|
return $this->_normalize($result); |
2751
|
|
|
} |
2752
|
|
|
|
2753
|
|
|
/** |
2754
|
|
|
* Logical Not. |
2755
|
|
|
* |
2756
|
|
|
* @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat> |
2757
|
|
|
* |
2758
|
|
|
* @return \Jose\Util\BigInteger |
2759
|
|
|
*/ |
2760
|
|
|
public function bitwise_not() |
2761
|
|
|
{ |
2762
|
|
|
// calculuate "not" without regard to $this->precision |
2763
|
|
|
// (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) |
2764
|
|
|
$temp = $this->toBytes(); |
2765
|
|
|
if ($temp == '') { |
2766
|
|
|
return ''; |
|
|
|
|
2767
|
|
|
} |
2768
|
|
|
$pre_msb = decbin(ord($temp[0])); |
2769
|
|
|
$temp = ~$temp; |
2770
|
|
|
$msb = decbin(ord($temp[0])); |
2771
|
|
|
if (strlen($msb) == 8) { |
2772
|
|
|
$msb = substr($msb, strpos($msb, '0')); |
2773
|
|
|
} |
2774
|
|
|
$temp[0] = chr(bindec($msb)); |
2775
|
|
|
|
2776
|
|
|
// see if we need to add extra leading 1's |
2777
|
|
|
$current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8; |
2778
|
|
|
$new_bits = $this->precision - $current_bits; |
2779
|
|
|
if ($new_bits <= 0) { |
2780
|
|
|
return $this->_normalize(new static($temp, 256)); |
2781
|
|
|
} |
2782
|
|
|
|
2783
|
|
|
// generate as many leading 1's as we need to. |
2784
|
|
|
$leading_ones = chr((1 << ($new_bits & 0x7)) - 1).str_repeat(chr(0xFF), $new_bits >> 3); |
2785
|
|
|
|
2786
|
|
|
self::_base256_lshift($leading_ones, $current_bits); |
2787
|
|
|
|
2788
|
|
|
$temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT); |
2789
|
|
|
|
2790
|
|
|
return $this->_normalize(new static($leading_ones | $temp, 256)); |
2791
|
|
|
} |
2792
|
|
|
|
2793
|
|
|
/** |
2794
|
|
|
* Logical Right Shift. |
2795
|
|
|
* |
2796
|
|
|
* Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. |
2797
|
|
|
* |
2798
|
|
|
* @param int $shift |
2799
|
|
|
* |
2800
|
|
|
* @return \Jose\Util\BigInteger |
2801
|
|
|
* |
2802
|
|
|
* @internal The only version that yields any speed increases is the internal version. |
2803
|
|
|
*/ |
2804
|
|
|
public function bitwise_rightShift($shift) |
2805
|
|
|
{ |
2806
|
|
|
$temp = new static(); |
2807
|
|
|
|
2808
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
2809
|
|
|
case self::MODE_GMP: |
2810
|
|
|
static $two; |
2811
|
|
|
|
2812
|
|
|
if (!isset($two)) { |
2813
|
|
|
$two = gmp_init('2'); |
2814
|
|
|
} |
2815
|
|
|
|
2816
|
|
|
$temp->value = gmp_div_q($this->value, gmp_pow($two, $shift)); |
|
|
|
|
2817
|
|
|
|
2818
|
|
|
break; |
2819
|
|
|
case self::MODE_BCMATH: |
2820
|
|
|
$temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0); |
|
|
|
|
2821
|
|
|
|
2822
|
|
|
break; |
2823
|
|
|
default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten |
2824
|
|
|
// and I don't want to do that... |
2825
|
|
|
$temp->value = $this->value; |
2826
|
|
|
$temp->_rshift($shift); |
2827
|
|
|
} |
2828
|
|
|
|
2829
|
|
|
return $this->_normalize($temp); |
2830
|
|
|
} |
2831
|
|
|
|
2832
|
|
|
/** |
2833
|
|
|
* Logical Left Shift. |
2834
|
|
|
* |
2835
|
|
|
* Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. |
2836
|
|
|
* |
2837
|
|
|
* @param int $shift |
2838
|
|
|
* |
2839
|
|
|
* @return \Jose\Util\BigInteger |
2840
|
|
|
* |
2841
|
|
|
* @internal The only version that yields any speed increases is the internal version. |
2842
|
|
|
*/ |
2843
|
|
|
public function bitwise_leftShift($shift) |
2844
|
|
|
{ |
2845
|
|
|
$temp = new static(); |
2846
|
|
|
|
2847
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
2848
|
|
|
case self::MODE_GMP: |
2849
|
|
|
static $two; |
2850
|
|
|
|
2851
|
|
|
if (!isset($two)) { |
2852
|
|
|
$two = gmp_init('2'); |
2853
|
|
|
} |
2854
|
|
|
|
2855
|
|
|
$temp->value = gmp_mul($this->value, gmp_pow($two, $shift)); |
|
|
|
|
2856
|
|
|
|
2857
|
|
|
break; |
2858
|
|
|
case self::MODE_BCMATH: |
2859
|
|
|
$temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0); |
|
|
|
|
2860
|
|
|
|
2861
|
|
|
break; |
2862
|
|
|
default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten |
2863
|
|
|
// and I don't want to do that... |
2864
|
|
|
$temp->value = $this->value; |
2865
|
|
|
$temp->_lshift($shift); |
2866
|
|
|
} |
2867
|
|
|
|
2868
|
|
|
return $this->_normalize($temp); |
2869
|
|
|
} |
2870
|
|
|
|
2871
|
|
|
/** |
2872
|
|
|
* Logical Left Rotate. |
2873
|
|
|
* |
2874
|
|
|
* Instead of the top x bits being dropped they're appended to the shifted bit string. |
2875
|
|
|
* |
2876
|
|
|
* @param int $shift |
2877
|
|
|
* |
2878
|
|
|
* @return \Jose\Util\BigInteger |
2879
|
|
|
*/ |
2880
|
|
|
public function bitwise_leftRotate($shift) |
2881
|
|
|
{ |
2882
|
|
|
$bits = $this->toBytes(); |
2883
|
|
|
|
2884
|
|
|
if ($this->precision > 0) { |
2885
|
|
|
$precision = $this->precision; |
2886
|
|
|
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { |
2887
|
|
|
$mask = $this->bitmask->subtract(new static(1)); |
|
|
|
|
2888
|
|
|
$mask = $mask->toBytes(); |
2889
|
|
|
} else { |
2890
|
|
|
$mask = $this->bitmask->toBytes(); |
|
|
|
|
2891
|
|
|
} |
2892
|
|
|
} else { |
2893
|
|
|
$temp = ord($bits[0]); |
2894
|
|
|
for ($i = 0; $temp >> $i; ++$i) { |
|
|
|
|
2895
|
|
|
} |
2896
|
|
|
$precision = 8 * strlen($bits) - 8 + $i; |
2897
|
|
|
$mask = chr((1 << ($precision & 0x7)) - 1).str_repeat(chr(0xFF), $precision >> 3); |
2898
|
|
|
} |
2899
|
|
|
|
2900
|
|
|
if ($shift < 0) { |
2901
|
|
|
$shift += $precision; |
2902
|
|
|
} |
2903
|
|
|
$shift %= $precision; |
2904
|
|
|
|
2905
|
|
|
if (!$shift) { |
2906
|
|
|
return clone $this; |
2907
|
|
|
} |
2908
|
|
|
|
2909
|
|
|
$left = $this->bitwise_leftShift($shift); |
2910
|
|
|
$left = $left->bitwise_and(new static($mask, 256)); |
2911
|
|
|
$right = $this->bitwise_rightShift($precision - $shift); |
2912
|
|
|
$result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right); |
2913
|
|
|
|
2914
|
|
|
return $this->_normalize($result); |
2915
|
|
|
} |
2916
|
|
|
|
2917
|
|
|
/** |
2918
|
|
|
* Logical Right Rotate. |
2919
|
|
|
* |
2920
|
|
|
* Instead of the bottom x bits being dropped they're prepended to the shifted bit string. |
2921
|
|
|
* |
2922
|
|
|
* @param int $shift |
2923
|
|
|
* |
2924
|
|
|
* @return \Jose\Util\BigInteger |
2925
|
|
|
*/ |
2926
|
|
|
public function bitwise_rightRotate($shift) |
2927
|
|
|
{ |
2928
|
|
|
return $this->bitwise_leftRotate(-$shift); |
2929
|
|
|
} |
2930
|
|
|
|
2931
|
|
|
/** |
2932
|
|
|
* Generates a random BigInteger. |
2933
|
|
|
* |
2934
|
|
|
* Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not. |
2935
|
|
|
* |
2936
|
|
|
* @param int $length |
|
|
|
|
2937
|
|
|
* |
2938
|
|
|
* @return \Jose\Util\BigInteger |
2939
|
|
|
*/ |
2940
|
|
|
private static function _random_number_helper($size) |
2941
|
|
|
{ |
2942
|
|
|
if (class_exists('\phpseclib\Crypt\Random')) { |
2943
|
|
|
$random = random_bytes($size); |
2944
|
|
|
} else { |
2945
|
|
|
$random = ''; |
2946
|
|
|
|
2947
|
|
|
if ($size & 1) { |
2948
|
|
|
$random .= chr(mt_rand(0, 255)); |
2949
|
|
|
} |
2950
|
|
|
|
2951
|
|
|
$blocks = $size >> 1; |
2952
|
|
|
for ($i = 0; $i < $blocks; ++$i) { |
2953
|
|
|
// mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems |
2954
|
|
|
$random .= pack('n', mt_rand(0, 0xFFFF)); |
2955
|
|
|
} |
2956
|
|
|
} |
2957
|
|
|
|
2958
|
|
|
return new static($random, 256); |
2959
|
|
|
} |
2960
|
|
|
|
2961
|
|
|
/** |
2962
|
|
|
* Generate a random number. |
2963
|
|
|
* |
2964
|
|
|
* Returns a random number between $min and $max where $min and $max |
2965
|
|
|
* can be defined using one of the two methods: |
2966
|
|
|
* |
2967
|
|
|
* BigInteger::random($min, $max) |
2968
|
|
|
* BigInteger::random($max, $min) |
2969
|
|
|
* |
2970
|
|
|
* @param \Jose\Util\eger $arg1 |
|
|
|
|
2971
|
|
|
* @param \Jose\Util\eger $arg2 |
|
|
|
|
2972
|
|
|
* |
2973
|
|
|
* @return \Jose\Util\BigInteger |
2974
|
|
|
*/ |
2975
|
|
|
public static function random(BigInteger $min, BigInteger $max) |
2976
|
|
|
{ |
2977
|
|
|
$compare = $max->compare($min); |
2978
|
|
|
|
2979
|
|
|
if (!$compare) { |
2980
|
|
|
return $this->_normalize($min); |
|
|
|
|
2981
|
|
|
} elseif ($compare < 0) { |
2982
|
|
|
// if $min is bigger then $max, swap $min and $max |
2983
|
|
|
$temp = $max; |
2984
|
|
|
$max = $min; |
2985
|
|
|
$min = $temp; |
2986
|
|
|
} |
2987
|
|
|
|
2988
|
|
|
static $one; |
2989
|
|
|
if (!isset($one)) { |
2990
|
|
|
$one = new static(1); |
2991
|
|
|
} |
2992
|
|
|
|
2993
|
|
|
$max = $max->subtract($min->subtract($one)); |
2994
|
|
|
$size = strlen(ltrim($max->toBytes(), chr(0))); |
2995
|
|
|
|
2996
|
|
|
/* |
2997
|
|
|
doing $random % $max doesn't work because some numbers will be more likely to occur than others. |
2998
|
|
|
eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145 |
2999
|
|
|
would produce 5 whereas the only value of random that could produce 139 would be 139. ie. |
3000
|
|
|
not all numbers would be equally likely. some would be more likely than others. |
3001
|
|
|
|
3002
|
|
|
creating a whole new random number until you find one that is within the range doesn't work |
3003
|
|
|
because, for sufficiently small ranges, the likelihood that you'd get a number within that range |
3004
|
|
|
would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability |
3005
|
|
|
would be pretty high that $random would be greater than $max. |
3006
|
|
|
|
3007
|
|
|
phpseclib works around this using the technique described here: |
3008
|
|
|
|
3009
|
|
|
http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string |
3010
|
|
|
*/ |
3011
|
|
|
$random_max = new static(chr(1).str_repeat("\0", $size), 256); |
3012
|
|
|
$random = static::_random_number_helper($size); |
|
|
|
|
3013
|
|
|
|
3014
|
|
|
list($max_multiple) = $random_max->divide($max); |
3015
|
|
|
$max_multiple = $max_multiple->multiply($max); |
3016
|
|
|
|
3017
|
|
|
while ($random->compare($max_multiple) >= 0) { |
3018
|
|
|
$random = $random->subtract($max_multiple); |
3019
|
|
|
$random_max = $random_max->subtract($max_multiple); |
3020
|
|
|
$random = $random->bitwise_leftShift(8); |
3021
|
|
|
$random = $random->add(self::_random_number_helper(1)); |
3022
|
|
|
$random_max = $random_max->bitwise_leftShift(8); |
3023
|
|
|
list($max_multiple) = $random_max->divide($max); |
3024
|
|
|
$max_multiple = $max_multiple->multiply($max); |
3025
|
|
|
} |
3026
|
|
|
list(, $random) = $random->divide($max); |
3027
|
|
|
|
3028
|
|
|
return $random->add($min); |
3029
|
|
|
} |
3030
|
|
|
|
3031
|
|
|
/** |
3032
|
|
|
* Generate a random prime number. |
3033
|
|
|
* |
3034
|
|
|
* If there's not a prime within the given range, false will be returned. |
3035
|
|
|
* If more than $timeout seconds have elapsed, give up and return false. |
3036
|
|
|
* |
3037
|
|
|
* @param \Jose\Util\teger $min |
3038
|
|
|
* @param \Jose\Util\teger $max |
3039
|
|
|
* @param int $timeout |
3040
|
|
|
* |
3041
|
|
|
* @return Math_BigInteger|false |
3042
|
|
|
* |
3043
|
|
|
* @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}. |
3044
|
|
|
*/ |
3045
|
|
|
public static function randomPrime(BigInteger $min, BigInteger $max, $timeout = false) |
3046
|
|
|
{ |
3047
|
|
|
$compare = $max->compare($min); |
3048
|
|
|
|
3049
|
|
|
if (!$compare) { |
3050
|
|
|
return $min->isPrime() ? $min : false; |
3051
|
|
|
} elseif ($compare < 0) { |
3052
|
|
|
// if $min is bigger then $max, swap $min and $max |
3053
|
|
|
$temp = $max; |
3054
|
|
|
$max = $min; |
3055
|
|
|
$min = $temp; |
3056
|
|
|
} |
3057
|
|
|
|
3058
|
|
|
static $one, $two; |
3059
|
|
|
if (!isset($one)) { |
3060
|
|
|
$one = new static(1); |
3061
|
|
|
$two = new static(2); |
3062
|
|
|
} |
3063
|
|
|
|
3064
|
|
|
$start = time(); |
3065
|
|
|
|
3066
|
|
|
$x = self::random($min, $max); |
3067
|
|
|
|
3068
|
|
|
// gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>. |
3069
|
|
|
if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) { |
3070
|
|
|
$p = new static(); |
3071
|
|
|
$p->value = gmp_nextprime($x->value); |
|
|
|
|
3072
|
|
|
|
3073
|
|
|
if ($p->compare($max) <= 0) { |
3074
|
|
|
return $p; |
3075
|
|
|
} |
3076
|
|
|
|
3077
|
|
|
if (!$min->equals($x)) { |
3078
|
|
|
$x = $x->subtract($one); |
3079
|
|
|
} |
3080
|
|
|
|
3081
|
|
|
return self::randomPrime($min, $x); |
3082
|
|
|
} |
3083
|
|
|
|
3084
|
|
|
if ($x->equals($two)) { |
3085
|
|
|
return $x; |
3086
|
|
|
} |
3087
|
|
|
|
3088
|
|
|
$x->_make_odd(); |
3089
|
|
|
if ($x->compare($max) > 0) { |
3090
|
|
|
// if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range |
3091
|
|
|
if ($min->equals($max)) { |
3092
|
|
|
return false; |
3093
|
|
|
} |
3094
|
|
|
$x = clone $min; |
3095
|
|
|
$x->_make_odd(); |
3096
|
|
|
} |
3097
|
|
|
|
3098
|
|
|
$initial_x = clone $x; |
3099
|
|
|
|
3100
|
|
|
while (true) { |
3101
|
|
|
if ($timeout !== false && time() - $start > $timeout) { |
3102
|
|
|
return false; |
3103
|
|
|
} |
3104
|
|
|
|
3105
|
|
|
if ($x->isPrime()) { |
3106
|
|
|
return $x; |
3107
|
|
|
} |
3108
|
|
|
|
3109
|
|
|
$x = $x->add($two); |
3110
|
|
|
|
3111
|
|
|
if ($x->compare($max) > 0) { |
3112
|
|
|
$x = clone $min; |
3113
|
|
|
if ($x->equals($two)) { |
3114
|
|
|
return $x; |
3115
|
|
|
} |
3116
|
|
|
$x->_make_odd(); |
3117
|
|
|
} |
3118
|
|
|
|
3119
|
|
|
if ($x->equals($initial_x)) { |
3120
|
|
|
return false; |
3121
|
|
|
} |
3122
|
|
|
} |
3123
|
|
|
} |
3124
|
|
|
|
3125
|
|
|
/** |
3126
|
|
|
* Make the current number odd. |
3127
|
|
|
* |
3128
|
|
|
* If the current number is odd it'll be unchanged. If it's even, one will be added to it. |
3129
|
|
|
*/ |
3130
|
|
|
private function _make_odd() |
3131
|
|
|
{ |
3132
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
3133
|
|
|
case self::MODE_GMP: |
3134
|
|
|
gmp_setbit($this->value, 0); |
3135
|
|
|
break; |
3136
|
|
|
case self::MODE_BCMATH: |
3137
|
|
|
if ($this->value[strlen($this->value) - 1] % 2 == 0) { |
3138
|
|
|
$this->value = bcadd($this->value, '1'); |
|
|
|
|
3139
|
|
|
} |
3140
|
|
|
break; |
3141
|
|
|
default: |
3142
|
|
|
$this->value[0] |= 1; |
3143
|
|
|
} |
3144
|
|
|
} |
3145
|
|
|
|
3146
|
|
|
/** |
3147
|
|
|
* Checks a numer to see if it's prime. |
3148
|
|
|
* |
3149
|
|
|
* Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the |
3150
|
|
|
* $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads |
3151
|
|
|
* on a website instead of just one. |
3152
|
|
|
* |
3153
|
|
|
* @param \Jose\Util\Integer $t |
3154
|
|
|
* |
3155
|
|
|
* @return bool |
3156
|
|
|
* |
3157
|
|
|
* @internal Uses the |
3158
|
|
|
* {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See |
3159
|
|
|
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}. |
3160
|
|
|
*/ |
3161
|
|
|
public function isPrime($t = false) |
3162
|
|
|
{ |
3163
|
|
|
$length = strlen($this->toBytes()); |
3164
|
|
|
|
3165
|
|
|
if (!$t) { |
3166
|
|
|
// see HAC 4.49 "Note (controlling the error probability)" |
3167
|
|
|
// @codingStandardsIgnoreStart |
3168
|
|
|
if ($length >= 163) { |
3169
|
|
|
$t = 2; |
3170
|
|
|
} // floor(1300 / 8) |
3171
|
|
|
elseif ($length >= 106) { |
3172
|
|
|
$t = 3; |
3173
|
|
|
} // floor( 850 / 8) |
3174
|
|
|
elseif ($length >= 81) { |
3175
|
|
|
$t = 4; |
3176
|
|
|
} // floor( 650 / 8) |
3177
|
|
|
elseif ($length >= 68) { |
3178
|
|
|
$t = 5; |
3179
|
|
|
} // floor( 550 / 8) |
3180
|
|
|
elseif ($length >= 56) { |
3181
|
|
|
$t = 6; |
3182
|
|
|
} // floor( 450 / 8) |
3183
|
|
|
elseif ($length >= 50) { |
3184
|
|
|
$t = 7; |
3185
|
|
|
} // floor( 400 / 8) |
3186
|
|
|
elseif ($length >= 43) { |
3187
|
|
|
$t = 8; |
3188
|
|
|
} // floor( 350 / 8) |
3189
|
|
|
elseif ($length >= 37) { |
3190
|
|
|
$t = 9; |
3191
|
|
|
} // floor( 300 / 8) |
3192
|
|
|
elseif ($length >= 31) { |
3193
|
|
|
$t = 12; |
3194
|
|
|
} // floor( 250 / 8) |
3195
|
|
|
elseif ($length >= 25) { |
3196
|
|
|
$t = 15; |
3197
|
|
|
} // floor( 200 / 8) |
3198
|
|
|
elseif ($length >= 18) { |
3199
|
|
|
$t = 18; |
3200
|
|
|
} // floor( 150 / 8) |
3201
|
|
|
else { |
3202
|
|
|
$t = 27; |
3203
|
|
|
} |
3204
|
|
|
// @codingStandardsIgnoreEnd |
3205
|
|
|
} |
3206
|
|
|
|
3207
|
|
|
// ie. gmp_testbit($this, 0) |
3208
|
|
|
// ie. isEven() or !isOdd() |
3209
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
3210
|
|
|
case self::MODE_GMP: |
3211
|
|
|
return gmp_prob_prime($this->value, $t) != 0; |
3212
|
|
|
case self::MODE_BCMATH: |
3213
|
|
|
if ($this->value === '2') { |
3214
|
|
|
return true; |
3215
|
|
|
} |
3216
|
|
|
if ($this->value[strlen($this->value) - 1] % 2 == 0) { |
3217
|
|
|
return false; |
3218
|
|
|
} |
3219
|
|
|
break; |
3220
|
|
|
default: |
3221
|
|
|
if ($this->value == [2]) { |
3222
|
|
|
return true; |
3223
|
|
|
} |
3224
|
|
|
if (~$this->value[0] & 1) { |
3225
|
|
|
return false; |
3226
|
|
|
} |
3227
|
|
|
} |
3228
|
|
|
|
3229
|
|
|
static $primes, $zero, $one, $two; |
3230
|
|
|
|
3231
|
|
|
if (!isset($primes)) { |
3232
|
|
|
$primes = [ |
3233
|
|
|
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, |
3234
|
|
|
61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, |
3235
|
|
|
139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, |
3236
|
|
|
229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, |
3237
|
|
|
317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, |
3238
|
|
|
421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, |
3239
|
|
|
521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, |
3240
|
|
|
619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, |
3241
|
|
|
733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, |
3242
|
|
|
839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, |
3243
|
|
|
953, 967, 971, 977, 983, 991, 997, |
3244
|
|
|
]; |
3245
|
|
|
|
3246
|
|
|
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { |
3247
|
|
|
for ($i = 0; $i < count($primes); ++$i) { |
|
|
|
|
3248
|
|
|
$primes[$i] = new static($primes[$i]); |
|
|
|
|
3249
|
|
|
} |
3250
|
|
|
} |
3251
|
|
|
|
3252
|
|
|
$zero = new static(); |
3253
|
|
|
$one = new static(1); |
3254
|
|
|
$two = new static(2); |
3255
|
|
|
} |
3256
|
|
|
|
3257
|
|
|
if ($this->equals($one)) { |
3258
|
|
|
return false; |
3259
|
|
|
} |
3260
|
|
|
|
3261
|
|
|
// see HAC 4.4.1 "Random search for probable primes" |
3262
|
|
|
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { |
3263
|
|
|
foreach ($primes as $prime) { |
3264
|
|
|
list(, $r) = $this->divide($prime); |
|
|
|
|
3265
|
|
|
if ($r->equals($zero)) { |
3266
|
|
|
return $this->equals($prime); |
|
|
|
|
3267
|
|
|
} |
3268
|
|
|
} |
3269
|
|
|
} else { |
3270
|
|
|
$value = $this->value; |
3271
|
|
|
foreach ($primes as $prime) { |
3272
|
|
|
list(, $r) = self::_divide_digit($value, $prime); |
|
|
|
|
3273
|
|
|
if (!$r) { |
3274
|
|
|
return count($value) == 1 && $value[0] == $prime; |
3275
|
|
|
} |
3276
|
|
|
} |
3277
|
|
|
} |
3278
|
|
|
|
3279
|
|
|
$n = clone $this; |
3280
|
|
|
$n_1 = $n->subtract($one); |
3281
|
|
|
$n_2 = $n->subtract($two); |
3282
|
|
|
|
3283
|
|
|
$r = clone $n_1; |
3284
|
|
|
$r_value = $r->value; |
3285
|
|
|
// ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); |
3286
|
|
|
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { |
3287
|
|
|
$s = 0; |
3288
|
|
|
// if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier |
3289
|
|
|
while ($r->value[strlen($r->value) - 1] % 2 == 0) { |
3290
|
|
|
$r->value = bcdiv($r->value, '2', 0); |
|
|
|
|
3291
|
|
|
++$s; |
3292
|
|
|
} |
3293
|
|
|
} else { |
3294
|
|
|
for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) { |
3295
|
|
|
$temp = ~$r_value[$i] & 0xFFFFFF; |
3296
|
|
|
for ($j = 1; ($temp >> $j) & 1; ++$j) { |
|
|
|
|
3297
|
|
|
} |
3298
|
|
|
if ($j != 25) { |
3299
|
|
|
break; |
3300
|
|
|
} |
3301
|
|
|
} |
3302
|
|
|
$s = 26 * $i + $j - 1; |
|
|
|
|
3303
|
|
|
$r->_rshift($s); |
3304
|
|
|
} |
3305
|
|
|
|
3306
|
|
|
for ($i = 0; $i < $t; ++$i) { |
3307
|
|
|
$a = self::random($two, $n_2); |
3308
|
|
|
$y = $a->modPow($r, $n); |
3309
|
|
|
|
3310
|
|
|
if (!$y->equals($one) && !$y->equals($n_1)) { |
3311
|
|
|
for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) { |
|
|
|
|
3312
|
|
|
$y = $y->modPow($two, $n); |
3313
|
|
|
if ($y->equals($one)) { |
3314
|
|
|
return false; |
3315
|
|
|
} |
3316
|
|
|
} |
3317
|
|
|
|
3318
|
|
|
if (!$y->equals($n_1)) { |
3319
|
|
|
return false; |
3320
|
|
|
} |
3321
|
|
|
} |
3322
|
|
|
} |
3323
|
|
|
|
3324
|
|
|
return true; |
3325
|
|
|
} |
3326
|
|
|
|
3327
|
|
|
/** |
3328
|
|
|
* Logical Left Shift. |
3329
|
|
|
* |
3330
|
|
|
* Shifts BigInteger's by $shift bits. |
3331
|
|
|
* |
3332
|
|
|
* @param int $shift |
3333
|
|
|
*/ |
3334
|
|
|
private function _lshift($shift) |
3335
|
|
|
{ |
3336
|
|
|
if ($shift == 0) { |
3337
|
|
|
return; |
3338
|
|
|
} |
3339
|
|
|
|
3340
|
|
|
$num_digits = (int) ($shift / self::$base); |
3341
|
|
|
$shift %= self::$base; |
3342
|
|
|
$shift = 1 << $shift; |
3343
|
|
|
|
3344
|
|
|
$carry = 0; |
3345
|
|
|
|
3346
|
|
|
for ($i = 0; $i < count($this->value); ++$i) { |
|
|
|
|
3347
|
|
|
$temp = $this->value[$i] * $shift + $carry; |
3348
|
|
|
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); |
3349
|
|
|
$this->value[$i] = (int) ($temp - $carry * self::$baseFull); |
3350
|
|
|
} |
3351
|
|
|
|
3352
|
|
|
if ($carry) { |
3353
|
|
|
$this->value[count($this->value)] = $carry; |
3354
|
|
|
} |
3355
|
|
|
|
3356
|
|
|
while ($num_digits--) { |
3357
|
|
|
array_unshift($this->value, 0); |
3358
|
|
|
} |
3359
|
|
|
} |
3360
|
|
|
|
3361
|
|
|
/** |
3362
|
|
|
* Logical Right Shift. |
3363
|
|
|
* |
3364
|
|
|
* Shifts BigInteger's by $shift bits. |
3365
|
|
|
* |
3366
|
|
|
* @param int $shift |
3367
|
|
|
*/ |
3368
|
|
|
private function _rshift($shift) |
3369
|
|
|
{ |
3370
|
|
|
if ($shift == 0) { |
3371
|
|
|
return; |
3372
|
|
|
} |
3373
|
|
|
|
3374
|
|
|
$num_digits = (int) ($shift / self::$base); |
3375
|
|
|
$shift %= self::$base; |
3376
|
|
|
$carry_shift = self::$base - $shift; |
3377
|
|
|
$carry_mask = (1 << $shift) - 1; |
3378
|
|
|
|
3379
|
|
|
if ($num_digits) { |
3380
|
|
|
$this->value = array_slice($this->value, $num_digits); |
3381
|
|
|
} |
3382
|
|
|
|
3383
|
|
|
$carry = 0; |
3384
|
|
|
|
3385
|
|
|
for ($i = count($this->value) - 1; $i >= 0; --$i) { |
3386
|
|
|
$temp = $this->value[$i] >> $shift | $carry; |
3387
|
|
|
$carry = ($this->value[$i] & $carry_mask) << $carry_shift; |
3388
|
|
|
$this->value[$i] = $temp; |
3389
|
|
|
} |
3390
|
|
|
|
3391
|
|
|
$this->value = $this->_trim($this->value); |
3392
|
|
|
} |
3393
|
|
|
|
3394
|
|
|
/** |
3395
|
|
|
* Normalize. |
3396
|
|
|
* |
3397
|
|
|
* Removes leading zeros and truncates (if necessary) to maintain the appropriate precision |
3398
|
|
|
* |
3399
|
|
|
* @param \Jose\Util\BigInteger |
3400
|
|
|
* |
3401
|
|
|
* @return \Jose\Util\BigInteger |
3402
|
|
|
*/ |
3403
|
|
|
private function _normalize($result) |
3404
|
|
|
{ |
3405
|
|
|
$result->precision = $this->precision; |
3406
|
|
|
$result->bitmask = $this->bitmask; |
3407
|
|
|
|
3408
|
|
|
switch (MATH_BIGINTEGER_MODE) { |
3409
|
|
|
case self::MODE_GMP: |
3410
|
|
|
if ($this->bitmask !== false) { |
3411
|
|
|
$result->value = gmp_and($result->value, $result->bitmask->value); |
3412
|
|
|
} |
3413
|
|
|
|
3414
|
|
|
return $result; |
3415
|
|
|
case self::MODE_BCMATH: |
3416
|
|
|
if (!empty($result->bitmask->value)) { |
3417
|
|
|
$result->value = bcmod($result->value, $result->bitmask->value); |
3418
|
|
|
} |
3419
|
|
|
|
3420
|
|
|
return $result; |
3421
|
|
|
} |
3422
|
|
|
|
3423
|
|
|
$value = &$result->value; |
3424
|
|
|
|
3425
|
|
|
if (!count($value)) { |
3426
|
|
|
return $result; |
3427
|
|
|
} |
3428
|
|
|
|
3429
|
|
|
$value = $this->_trim($value); |
3430
|
|
|
|
3431
|
|
|
if (!empty($result->bitmask->value)) { |
3432
|
|
|
$length = min(count($value), count($this->bitmask->value)); |
3433
|
|
|
$value = array_slice($value, 0, $length); |
3434
|
|
|
|
3435
|
|
|
for ($i = 0; $i < $length; ++$i) { |
3436
|
|
|
$value[$i] = $value[$i] & $this->bitmask->value[$i]; |
3437
|
|
|
} |
3438
|
|
|
} |
3439
|
|
|
|
3440
|
|
|
return $result; |
3441
|
|
|
} |
3442
|
|
|
|
3443
|
|
|
/** |
3444
|
|
|
* Trim. |
3445
|
|
|
* |
3446
|
|
|
* Removes leading zeros |
3447
|
|
|
* |
3448
|
|
|
* @param array $value |
3449
|
|
|
* |
3450
|
|
|
* @return \Jose\Util\BigInteger |
3451
|
|
|
*/ |
3452
|
|
|
private static function _trim($value) |
3453
|
|
|
{ |
3454
|
|
|
for ($i = count($value) - 1; $i >= 0; --$i) { |
3455
|
|
|
if ($value[$i]) { |
3456
|
|
|
break; |
3457
|
|
|
} |
3458
|
|
|
unset($value[$i]); |
3459
|
|
|
} |
3460
|
|
|
|
3461
|
|
|
return $value; |
3462
|
|
|
} |
3463
|
|
|
|
3464
|
|
|
/** |
3465
|
|
|
* Array Repeat. |
3466
|
|
|
* |
3467
|
|
|
* @param $input Array |
3468
|
|
|
* @param $multiplier mixed |
3469
|
|
|
* |
3470
|
|
|
* @return array |
3471
|
|
|
*/ |
3472
|
|
|
private static function _array_repeat($input, $multiplier) |
3473
|
|
|
{ |
3474
|
|
|
return ($multiplier) ? array_fill(0, $multiplier, $input) : []; |
3475
|
|
|
} |
3476
|
|
|
|
3477
|
|
|
/** |
3478
|
|
|
* Logical Left Shift. |
3479
|
|
|
* |
3480
|
|
|
* Shifts binary strings $shift bits, essentially multiplying by 2**$shift. |
3481
|
|
|
* |
3482
|
|
|
* @param $x String |
3483
|
|
|
* @param $shift Integer |
3484
|
|
|
* |
3485
|
|
|
* @return string |
3486
|
|
|
*/ |
3487
|
|
|
private static function _base256_lshift(&$x, $shift) |
3488
|
|
|
{ |
3489
|
|
|
if ($shift == 0) { |
3490
|
|
|
return; |
3491
|
|
|
} |
3492
|
|
|
|
3493
|
|
|
$num_bytes = $shift >> 3; // eg. floor($shift/8) |
3494
|
|
|
$shift &= 7; // eg. $shift % 8 |
3495
|
|
|
|
3496
|
|
|
$carry = 0; |
3497
|
|
|
for ($i = strlen($x) - 1; $i >= 0; --$i) { |
3498
|
|
|
$temp = ord($x[$i]) << $shift | $carry; |
3499
|
|
|
$x[$i] = chr($temp); |
3500
|
|
|
$carry = $temp >> 8; |
3501
|
|
|
} |
3502
|
|
|
$carry = ($carry != 0) ? chr($carry) : ''; |
3503
|
|
|
$x = $carry.$x.str_repeat(chr(0), $num_bytes); |
3504
|
|
|
} |
3505
|
|
|
|
3506
|
|
|
/** |
3507
|
|
|
* Logical Right Shift. |
3508
|
|
|
* |
3509
|
|
|
* Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder. |
3510
|
|
|
* |
3511
|
|
|
* @param $x String |
3512
|
|
|
* @param $shift Integer |
3513
|
|
|
* |
3514
|
|
|
* @return string |
3515
|
|
|
*/ |
3516
|
|
|
private static function _base256_rshift(&$x, $shift) |
3517
|
|
|
{ |
3518
|
|
|
if ($shift == 0) { |
3519
|
|
|
$x = ltrim($x, chr(0)); |
3520
|
|
|
|
3521
|
|
|
return ''; |
3522
|
|
|
} |
3523
|
|
|
|
3524
|
|
|
$num_bytes = $shift >> 3; // eg. floor($shift/8) |
3525
|
|
|
$shift &= 7; // eg. $shift % 8 |
3526
|
|
|
|
3527
|
|
|
$remainder = ''; |
3528
|
|
|
if ($num_bytes) { |
3529
|
|
|
$start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes; |
3530
|
|
|
$remainder = substr($x, $start); |
3531
|
|
|
$x = substr($x, 0, -$num_bytes); |
3532
|
|
|
} |
3533
|
|
|
|
3534
|
|
|
$carry = 0; |
3535
|
|
|
$carry_shift = 8 - $shift; |
3536
|
|
|
for ($i = 0; $i < strlen($x); ++$i) { |
|
|
|
|
3537
|
|
|
$temp = (ord($x[$i]) >> $shift) | $carry; |
3538
|
|
|
$carry = (ord($x[$i]) << $carry_shift) & 0xFF; |
3539
|
|
|
$x[$i] = chr($temp); |
3540
|
|
|
} |
3541
|
|
|
$x = ltrim($x, chr(0)); |
3542
|
|
|
|
3543
|
|
|
$remainder = chr($carry >> $carry_shift).$remainder; |
3544
|
|
|
|
3545
|
|
|
return ltrim($remainder, chr(0)); |
3546
|
|
|
} |
3547
|
|
|
|
3548
|
|
|
// one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long |
3549
|
|
|
// at 32-bits, while java's longs are 64-bits. |
3550
|
|
|
|
3551
|
|
|
/** |
3552
|
|
|
* Converts 32-bit integers to bytes. |
3553
|
|
|
* |
3554
|
|
|
* @param int $x |
3555
|
|
|
* |
3556
|
|
|
* @return string |
3557
|
|
|
*/ |
3558
|
|
|
private static function _int2bytes($x) |
3559
|
|
|
{ |
3560
|
|
|
return ltrim(pack('N', $x), chr(0)); |
3561
|
|
|
} |
3562
|
|
|
|
3563
|
|
|
/** |
3564
|
|
|
* Converts bytes to 32-bit integers. |
3565
|
|
|
* |
3566
|
|
|
* @param string $x |
3567
|
|
|
* |
3568
|
|
|
* @return int |
3569
|
|
|
*/ |
3570
|
|
|
private static function _bytes2int($x) |
3571
|
|
|
{ |
3572
|
|
|
$temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT)); |
3573
|
|
|
|
3574
|
|
|
return $temp['int']; |
3575
|
|
|
} |
3576
|
|
|
|
3577
|
|
|
/** |
3578
|
|
|
* DER-encode an integer. |
3579
|
|
|
* |
3580
|
|
|
* The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL |
3581
|
|
|
* |
3582
|
|
|
* @param int $length |
3583
|
|
|
* |
3584
|
|
|
* @return string |
3585
|
|
|
*/ |
3586
|
|
|
private static function _encodeASN1Length($length) |
3587
|
|
|
{ |
3588
|
|
|
if ($length <= 0x7F) { |
3589
|
|
|
return chr($length); |
3590
|
|
|
} |
3591
|
|
|
|
3592
|
|
|
$temp = ltrim(pack('N', $length), chr(0)); |
3593
|
|
|
|
3594
|
|
|
return pack('Ca*', 0x80 | strlen($temp), $temp); |
3595
|
|
|
} |
3596
|
|
|
|
3597
|
|
|
/** |
3598
|
|
|
* Single digit division. |
3599
|
|
|
* |
3600
|
|
|
* Even if int64 is being used the division operator will return a float64 value |
3601
|
|
|
* if the dividend is not evenly divisible by the divisor. Since a float64 doesn't |
3602
|
|
|
* have the precision of int64 this is a problem so, when int64 is being used, |
3603
|
|
|
* we'll guarantee that the dividend is divisible by first subtracting the remainder. |
3604
|
|
|
* |
3605
|
|
|
* @param int $x |
3606
|
|
|
* @param int $y |
3607
|
|
|
* |
3608
|
|
|
* @return int |
3609
|
|
|
*/ |
3610
|
|
|
private static function _safe_divide($x, $y) |
3611
|
|
|
{ |
3612
|
|
|
if (self::$base === 26) { |
3613
|
|
|
return (int) ($x / $y); |
3614
|
|
|
} |
3615
|
|
|
|
3616
|
|
|
// self::$base === 31 |
3617
|
|
|
return ($x - ($x % $y)) / $y; |
3618
|
|
|
} |
3619
|
|
|
} |
3620
|
|
|
|
Adding a
@return
annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.Please refer to the PHP core documentation on constructors.