PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / lib / packages / Brick / Math / BigNumber.php

BigNumber.php in Defender Security – Malware Scanner, Login Security & Firewall 6.3.0, at lib/packages/Brick/Math/BigNumber.php

573 lines 16.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace WP_DEFENDER_VENDOR\Brick\Math;
6
7 use WP_DEFENDER_VENDOR\Brick\Math\Exception\DivisionByZeroException;
8 use WP_DEFENDER_VENDOR\Brick\Math\Exception\MathException;
9 use WP_DEFENDER_VENDOR\Brick\Math\Exception\NumberFormatException;
10 use WP_DEFENDER_VENDOR\Brick\Math\Exception\RoundingNecessaryException;
11
12 /**
13 * Common interface for arbitrary-precision rational numbers.
14 *
15 * @psalm-immutable
16 */
17 abstract class BigNumber implements \Serializable, \JsonSerializable
18 {
19 /**
20 * The regular expression used to parse integer, decimal and rational numbers.
21 */
22 private const PARSE_REGEXP =
23 '/^' .
24 '(?<sign>[\-\+])?' .
25 '(?:' .
26 '(?:' .
27 '(?<integral>[0-9]+)?' .
28 '(?<point>\.)?' .
29 '(?<fractional>[0-9]+)?' .
30 '(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
31 ')|(?:' .
32 '(?<numerator>[0-9]+)' .
33 '\/?' .
34 '(?<denominator>[0-9]+)' .
35 ')' .
36 ')' .
37 '$/';
38
39 /**
40 * Creates a BigNumber of the given value.
41 *
42 * The concrete return type is dependent on the given value, with the following rules:
43 *
44 * - BigNumber instances are returned as is
45 * - integer numbers are returned as BigInteger
46 * - floating point numbers are converted to a string then parsed as such
47 * - strings containing a `/` character are returned as BigRational
48 * - strings containing a `.` character or using an exponential notation are returned as BigDecimal
49 * - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
50 *
51 * @param BigNumber|int|float|string $value
52 *
53 * @return BigNumber
54 *
55 * @throws NumberFormatException If the format of the number is not valid.
56 * @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
57 *
58 * @psalm-pure
59 */
60 public static function of($value) : BigNumber
61 {
62 if ($value instanceof BigNumber) {
63 return $value;
64 }
65
66 if (\is_int($value)) {
67 return new BigInteger((string) $value);
68 }
69
70 /** @psalm-suppress RedundantCastGivenDocblockType We cannot trust the untyped $value here! */
71 $value = \is_float($value) ? self::floatToString($value) : (string) $value;
72
73 $throw = static function() use ($value) : void {
74 throw new NumberFormatException(\sprintf(
75 'The given value "%s" does not represent a valid number.',
76 $value
77 ));
78 };
79
80 if (\preg_match(self::PARSE_REGEXP, $value, $matches) !== 1) {
81 $throw();
82 }
83
84 $getMatch = static function(string $value) use ($matches) : ?string {
85 return isset($matches[$value]) && $matches[$value] !== '' ? $matches[$value] : null;
86 };
87
88 $sign = $getMatch('sign');
89 $numerator = $getMatch('numerator');
90 $denominator = $getMatch('denominator');
91
92 if ($numerator !== null) {
93 assert($denominator !== null);
94
95 if ($sign !== null) {
96 $numerator = $sign . $numerator;
97 }
98
99 $numerator = self::cleanUp($numerator);
100 $denominator = self::cleanUp($denominator);
101
102 if ($denominator === '0') {
103 throw DivisionByZeroException::denominatorMustNotBeZero();
104 }
105
106 return new BigRational(
107 new BigInteger($numerator),
108 new BigInteger($denominator),
109 false
110 );
111 }
112
113 $point = $getMatch('point');
114 $integral = $getMatch('integral');
115 $fractional = $getMatch('fractional');
116 $exponent = $getMatch('exponent');
117
118 if ($integral === null && $fractional === null) {
119 $throw();
120 }
121
122 if ($integral === null) {
123 $integral = '0';
124 }
125
126 if ($point !== null || $exponent !== null) {
127 $fractional = ($fractional ?? '');
128 $exponent = ($exponent !== null) ? (int) $exponent : 0;
129
130 if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) {
131 throw new NumberFormatException('Exponent too large.');
132 }
133
134 $unscaledValue = self::cleanUp(($sign ?? ''). $integral . $fractional);
135
136 $scale = \strlen($fractional) - $exponent;
137
138 if ($scale < 0) {
139 if ($unscaledValue !== '0') {
140 $unscaledValue .= \str_repeat('0', - $scale);
141 }
142 $scale = 0;
143 }
144
145 return new BigDecimal($unscaledValue, $scale);
146 }
147
148 $integral = self::cleanUp(($sign ?? '') . $integral);
149
150 return new BigInteger($integral);
151 }
152
153 /**
154 * Safely converts float to string, avoiding locale-dependent issues.
155 *
156 * @see https://github.com/brick/math/pull/20
157 *
158 * @param float $float
159 *
160 * @return string
161 *
162 * @psalm-pure
163 * @psalm-suppress ImpureFunctionCall
164 */
165 private static function floatToString(float $float) : string
166 {
167 $currentLocale = \setlocale(LC_NUMERIC, '0');
168 \setlocale(LC_NUMERIC, 'C');
169
170 $result = (string) $float;
171
172 \setlocale(LC_NUMERIC, $currentLocale);
173
174 return $result;
175 }
176
177 /**
178 * Proxy method to access protected constructors from sibling classes.
179 *
180 * @internal
181 *
182 * @param mixed ...$args The arguments to the constructor.
183 *
184 * @return static
185 *
186 * @psalm-pure
187 * @psalm-suppress TooManyArguments
188 * @psalm-suppress UnsafeInstantiation
189 */
190 protected static function create(... $args) : BigNumber
191 {
192 return new static(... $args);
193 }
194
195 /**
196 * Returns the minimum of the given values.
197 *
198 * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
199 * to an instance of the class this method is called on.
200 *
201 * @return static The minimum value.
202 *
203 * @throws \InvalidArgumentException If no values are given.
204 * @throws MathException If an argument is not valid.
205 *
206 * @psalm-suppress LessSpecificReturnStatement
207 * @psalm-suppress MoreSpecificReturnType
208 * @psalm-pure
209 */
210 public static function min(...$values) : BigNumber
211 {
212 $min = null;
213
214 foreach ($values as $value) {
215 $value = static::of($value);
216
217 if ($min === null || $value->isLessThan($min)) {
218 $min = $value;
219 }
220 }
221
222 if ($min === null) {
223 throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
224 }
225
226 return $min;
227 }
228
229 /**
230 * Returns the maximum of the given values.
231 *
232 * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
233 * to an instance of the class this method is called on.
234 *
235 * @return static The maximum value.
236 *
237 * @throws \InvalidArgumentException If no values are given.
238 * @throws MathException If an argument is not valid.
239 *
240 * @psalm-suppress LessSpecificReturnStatement
241 * @psalm-suppress MoreSpecificReturnType
242 * @psalm-pure
243 */
244 public static function max(...$values) : BigNumber
245 {
246 $max = null;
247
248 foreach ($values as $value) {
249 $value = static::of($value);
250
251 if ($max === null || $value->isGreaterThan($max)) {
252 $max = $value;
253 }
254 }
255
256 if ($max === null) {
257 throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
258 }
259
260 return $max;
261 }
262
263 /**
264 * Returns the sum of the given values.
265 *
266 * @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible
267 * to an instance of the class this method is called on.
268 *
269 * @return static The sum.
270 *
271 * @throws \InvalidArgumentException If no values are given.
272 * @throws MathException If an argument is not valid.
273 *
274 * @psalm-suppress LessSpecificReturnStatement
275 * @psalm-suppress MoreSpecificReturnType
276 * @psalm-pure
277 */
278 public static function sum(...$values) : BigNumber
279 {
280 /** @var BigNumber|null $sum */
281 $sum = null;
282
283 foreach ($values as $value) {
284 $value = static::of($value);
285
286 $sum = $sum === null ? $value : self::add($sum, $value);
287 }
288
289 if ($sum === null) {
290 throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
291 }
292
293 return $sum;
294 }
295
296 /**
297 * Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
298 *
299 * @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to
300 * concrete classes the responsibility to perform the addition themselves or delegate it to the given number,
301 * depending on their ability to perform the operation. This will also require a version bump because we're
302 * potentially breaking custom BigNumber implementations (if any...)
303 *
304 * @param BigNumber $a
305 * @param BigNumber $b
306 *
307 * @return BigNumber
308 *
309 * @psalm-pure
310 */
311 private static function add(BigNumber $a, BigNumber $b) : BigNumber
312 {
313 if ($a instanceof BigRational) {
314 return $a->plus($b);
315 }
316
317 if ($b instanceof BigRational) {
318 return $b->plus($a);
319 }
320
321 if ($a instanceof BigDecimal) {
322 return $a->plus($b);
323 }
324
325 if ($b instanceof BigDecimal) {
326 return $b->plus($a);
327 }
328
329 /** @var BigInteger $a */
330
331 return $a->plus($b);
332 }
333
334 /**
335 * Removes optional leading zeros and + sign from the given number.
336 *
337 * @param string $number The number, validated as a non-empty string of digits with optional leading sign.
338 *
339 * @return string
340 *
341 * @psalm-pure
342 */
343 private static function cleanUp(string $number) : string
344 {
345 $firstChar = $number[0];
346
347 if ($firstChar === '+' || $firstChar === '-') {
348 $number = \substr($number, 1);
349 }
350
351 $number = \ltrim($number, '0');
352
353 if ($number === '') {
354 return '0';
355 }
356
357 if ($firstChar === '-') {
358 return '-' . $number;
359 }
360
361 return $number;
362 }
363
364 /**
365 * Checks if this number is equal to the given one.
366 *
367 * @param BigNumber|int|float|string $that
368 *
369 * @return bool
370 */
371 public function isEqualTo($that) : bool
372 {
373 return $this->compareTo($that) === 0;
374 }
375
376 /**
377 * Checks if this number is strictly lower than the given one.
378 *
379 * @param BigNumber|int|float|string $that
380 *
381 * @return bool
382 */
383 public function isLessThan($that) : bool
384 {
385 return $this->compareTo($that) < 0;
386 }
387
388 /**
389 * Checks if this number is lower than or equal to the given one.
390 *
391 * @param BigNumber|int|float|string $that
392 *
393 * @return bool
394 */
395 public function isLessThanOrEqualTo($that) : bool
396 {
397 return $this->compareTo($that) <= 0;
398 }
399
400 /**
401 * Checks if this number is strictly greater than the given one.
402 *
403 * @param BigNumber|int|float|string $that
404 *
405 * @return bool
406 */
407 public function isGreaterThan($that) : bool
408 {
409 return $this->compareTo($that) > 0;
410 }
411
412 /**
413 * Checks if this number is greater than or equal to the given one.
414 *
415 * @param BigNumber|int|float|string $that
416 *
417 * @return bool
418 */
419 public function isGreaterThanOrEqualTo($that) : bool
420 {
421 return $this->compareTo($that) >= 0;
422 }
423
424 /**
425 * Checks if this number equals zero.
426 *
427 * @return bool
428 */
429 public function isZero() : bool
430 {
431 return $this->getSign() === 0;
432 }
433
434 /**
435 * Checks if this number is strictly negative.
436 *
437 * @return bool
438 */
439 public function isNegative() : bool
440 {
441 return $this->getSign() < 0;
442 }
443
444 /**
445 * Checks if this number is negative or zero.
446 *
447 * @return bool
448 */
449 public function isNegativeOrZero() : bool
450 {
451 return $this->getSign() <= 0;
452 }
453
454 /**
455 * Checks if this number is strictly positive.
456 *
457 * @return bool
458 */
459 public function isPositive() : bool
460 {
461 return $this->getSign() > 0;
462 }
463
464 /**
465 * Checks if this number is positive or zero.
466 *
467 * @return bool
468 */
469 public function isPositiveOrZero() : bool
470 {
471 return $this->getSign() >= 0;
472 }
473
474 /**
475 * Returns the sign of this number.
476 *
477 * @return int -1 if the number is negative, 0 if zero, 1 if positive.
478 */
479 abstract public function getSign() : int;
480
481 /**
482 * Compares this number to the given one.
483 *
484 * @param BigNumber|int|float|string $that
485 *
486 * @return int [-1,0,1] If `$this` is lower than, equal to, or greater than `$that`.
487 *
488 * @throws MathException If the number is not valid.
489 */
490 abstract public function compareTo($that) : int;
491
492 /**
493 * Converts this number to a BigInteger.
494 *
495 * @return BigInteger The converted number.
496 *
497 * @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
498 */
499 abstract public function toBigInteger() : BigInteger;
500
501 /**
502 * Converts this number to a BigDecimal.
503 *
504 * @return BigDecimal The converted number.
505 *
506 * @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
507 */
508 abstract public function toBigDecimal() : BigDecimal;
509
510 /**
511 * Converts this number to a BigRational.
512 *
513 * @return BigRational The converted number.
514 */
515 abstract public function toBigRational() : BigRational;
516
517 /**
518 * Converts this number to a BigDecimal with the given scale, using rounding if necessary.
519 *
520 * @param int $scale The scale of the resulting `BigDecimal`.
521 * @param int $roundingMode A `RoundingMode` constant.
522 *
523 * @return BigDecimal
524 *
525 * @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding.
526 * This only applies when RoundingMode::UNNECESSARY is used.
527 */
528 abstract public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;
529
530 /**
531 * Returns the exact value of this number as a native integer.
532 *
533 * If this number cannot be converted to a native integer without losing precision, an exception is thrown.
534 * Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
535 *
536 * @return int The converted value.
537 *
538 * @throws MathException If this number cannot be exactly converted to a native integer.
539 */
540 abstract public function toInt() : int;
541
542 /**
543 * Returns an approximation of this number as a floating-point value.
544 *
545 * Note that this method can discard information as the precision of a floating-point value
546 * is inherently limited.
547 *
548 * If the number is greater than the largest representable floating point number, positive infinity is returned.
549 * If the number is less than the smallest representable floating point number, negative infinity is returned.
550 *
551 * @return float The converted value.
552 */
553 abstract public function toFloat() : float;
554
555 /**
556 * Returns a string representation of this number.
557 *
558 * The output of this method can be parsed by the `of()` factory method;
559 * this will yield an object equal to this one, without any information loss.
560 *
561 * @return string
562 */
563 abstract public function __toString() : string;
564
565 /**
566 * {@inheritdoc}
567 */
568 public function jsonSerialize() : string
569 {
570 return $this->__toString();
571 }
572 }
573