Файловый менеджер - Редактировать - /home/clickysoft/public_html/jmapi5.clickysoft.net/brick.zip
Назад
PK !>�Z��E+� � math/composer.jsonnu �[��� { "name": "brick/math", "description": "Arbitrary-precision arithmetic library", "type": "library", "keywords": [ "Brick", "Math", "Arbitrary-precision", "Arithmetic", "BigInteger", "BigDecimal", "BigRational", "Bignum" ], "license": "MIT", "require": { "php": "^8.0" }, "require-dev": { "phpunit/phpunit": "^9.0", "php-coveralls/php-coveralls": "^2.2", "vimeo/psalm": "5.0.0" }, "autoload": { "psr-4": { "Brick\\Math\\": "src/" } }, "autoload-dev": { "psr-4": { "Brick\\Math\\Tests\\": "tests/" } } } PK !>�Z�Ve�WF WF math/CHANGELOG.mdnu �[��� # Changelog All notable changes to this project will be documented in this file. ## [0.11.0](https://github.com/brick/math/releases/tag/0.11.0) - 2023-01-16 💥 **Breaking changes** - Minimum PHP version is now 8.0 - Methods accepting a union of types are now strongly typed<sup>*</sup> - `MathException` now extends `Exception` instead of `RuntimeException` <sup>* You may now run into type errors if you were passing `Stringable` objects to `of()` or any of the methods internally calling `of()`, with `strict_types` enabled. You can fix this by casting `Stringable` objects to `string` first.</sup> ## [0.10.2](https://github.com/brick/math/releases/tag/0.10.2) - 2022-08-11 👌 **Improvements** - `BigRational::toFloat()` now simplifies the fraction before performing division (#73) thanks to @olsavmic ## [0.10.1](https://github.com/brick/math/releases/tag/0.10.1) - 2022-08-02 ✨ **New features** - `BigInteger::gcdMultiple()` returns the GCD of multiple `BigInteger` numbers ## [0.10.0](https://github.com/brick/math/releases/tag/0.10.0) - 2022-06-18 💥 **Breaking changes** - Minimum PHP version is now 7.4 ## [0.9.3](https://github.com/brick/math/releases/tag/0.9.3) - 2021-08-15 🚀 **Compatibility with PHP 8.1** - Support for custom object serialization; this removes a warning on PHP 8.1 due to the `Serializable` interface being deprecated (#60) thanks @TRowbotham ## [0.9.2](https://github.com/brick/math/releases/tag/0.9.2) - 2021-01-20 🐛 **Bug fix** - Incorrect results could be returned when using the BCMath calculator, with a default scale set with `bcscale()`, on PHP >= 7.2 (#55). ## [0.9.1](https://github.com/brick/math/releases/tag/0.9.1) - 2020-08-19 ✨ **New features** - `BigInteger::not()` returns the bitwise `NOT` value 🐛 **Bug fixes** - `BigInteger::toBytes()` could return an incorrect binary representation for some numbers - The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available ## [0.9.0](https://github.com/brick/math/releases/tag/0.9.0) - 2020-08-18 👌 **Improvements** - `BigNumber::of()` now accepts `.123` and `123.` formats, both of which return a `BigDecimal` 💥 **Breaking changes** - Deprecated method `BigInteger::powerMod()` has been removed - use `modPow()` instead - Deprecated method `BigInteger::parse()` has been removed - use `fromBase()` instead ## [0.8.17](https://github.com/brick/math/releases/tag/0.8.17) - 2020-08-19 🐛 **Bug fix** - `BigInteger::toBytes()` could return an incorrect binary representation for some numbers - The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available ## [0.8.16](https://github.com/brick/math/releases/tag/0.8.16) - 2020-08-18 🚑 **Critical fix** - This version reintroduces the deprecated `BigInteger::parse()` method, that has been removed by mistake in version `0.8.9` and should have lasted for the whole `0.8` release cycle. ✨ **New features** - `BigInteger::modInverse()` calculates a modular multiplicative inverse - `BigInteger::fromBytes()` creates a `BigInteger` from a byte string - `BigInteger::toBytes()` converts a `BigInteger` to a byte string - `BigInteger::randomBits()` creates a pseudo-random `BigInteger` of a given bit length - `BigInteger::randomRange()` creates a pseudo-random `BigInteger` between two bounds 💩 **Deprecations** - `BigInteger::powerMod()` is now deprecated in favour of `modPow()` ## [0.8.15](https://github.com/brick/math/releases/tag/0.8.15) - 2020-04-15 🐛 **Fixes** - added missing `ext-json` requirement, due to `BigNumber` implementing `JsonSerializable` ⚡️ **Optimizations** - additional optimization in `BigInteger::remainder()` ## [0.8.14](https://github.com/brick/math/releases/tag/0.8.14) - 2020-02-18 ✨ **New features** - `BigInteger::getLowestSetBit()` returns the index of the rightmost one bit ## [0.8.13](https://github.com/brick/math/releases/tag/0.8.13) - 2020-02-16 ✨ **New features** - `BigInteger::isEven()` tests whether the number is even - `BigInteger::isOdd()` tests whether the number is odd - `BigInteger::testBit()` tests if a bit is set - `BigInteger::getBitLength()` returns the number of bits in the minimal representation of the number ## [0.8.12](https://github.com/brick/math/releases/tag/0.8.12) - 2020-02-03 🛠️ **Maintenance release** Classes are now annotated for better static analysis with [psalm](https://psalm.dev/). This is a maintenance release: no bug fixes, no new features, no breaking changes. ## [0.8.11](https://github.com/brick/math/releases/tag/0.8.11) - 2020-01-23 ✨ **New feature** `BigInteger::powerMod()` performs a power-with-modulo operation. Useful for crypto. ## [0.8.10](https://github.com/brick/math/releases/tag/0.8.10) - 2020-01-21 ✨ **New feature** `BigInteger::mod()` returns the **modulo** of two numbers. The *modulo* differs from the *remainder* when the signs of the operands are different. ## [0.8.9](https://github.com/brick/math/releases/tag/0.8.9) - 2020-01-08 ⚡️ **Performance improvements** A few additional optimizations in `BigInteger` and `BigDecimal` when one of the operands can be returned as is. Thanks to @tomtomsen in #24. ## [0.8.8](https://github.com/brick/math/releases/tag/0.8.8) - 2019-04-25 🐛 **Bug fixes** - `BigInteger::toBase()` could return an empty string for zero values (BCMath & Native calculators only, GMP calculator unaffected) ✨ **New features** - `BigInteger::toArbitraryBase()` converts a number to an arbitrary base, using a custom alphabet - `BigInteger::fromArbitraryBase()` converts a string in an arbitrary base, using a custom alphabet, back to a number These methods can be used as the foundation to convert strings between different bases/alphabets, using BigInteger as an intermediate representation. 💩 **Deprecations** - `BigInteger::parse()` is now deprecated in favour of `fromBase()` `BigInteger::fromBase()` works the same way as `parse()`, with 2 minor differences: - the `$base` parameter is required, it does not default to `10` - it throws a `NumberFormatException` instead of an `InvalidArgumentException` when the number is malformed ## [0.8.7](https://github.com/brick/math/releases/tag/0.8.7) - 2019-04-20 **Improvements** - Safer conversion from `float` when using custom locales - **Much faster** `NativeCalculator` implementation 🚀 You can expect **at least a 3x performance improvement** for common arithmetic operations when using the library on systems without GMP or BCMath; it gets exponentially faster on multiplications with a high number of digits. This is due to calculations now being performed on whole blocks of digits (the block size depending on the platform, 32-bit or 64-bit) instead of digit-by-digit as before. ## [0.8.6](https://github.com/brick/math/releases/tag/0.8.6) - 2019-04-11 **New method** `BigNumber::sum()` returns the sum of one or more numbers. ## [0.8.5](https://github.com/brick/math/releases/tag/0.8.5) - 2019-02-12 **Bug fix**: `of()` factory methods could fail when passing a `float` in environments using a `LC_NUMERIC` locale with a decimal separator other than `'.'` (#20). Thanks @manowark 👍 ## [0.8.4](https://github.com/brick/math/releases/tag/0.8.4) - 2018-12-07 **New method** `BigDecimal::sqrt()` calculates the square root of a decimal number, to a given scale. ## [0.8.3](https://github.com/brick/math/releases/tag/0.8.3) - 2018-12-06 **New method** `BigInteger::sqrt()` calculates the square root of a number (thanks @peter279k). **New exception** `NegativeNumberException` is thrown when calling `sqrt()` on a negative number. ## [0.8.2](https://github.com/brick/math/releases/tag/0.8.2) - 2018-11-08 **Performance update** - Further improvement of `toInt()` performance - `NativeCalculator` can now perform some multiplications more efficiently ## [0.8.1](https://github.com/brick/math/releases/tag/0.8.1) - 2018-11-07 Performance optimization of `toInt()` methods. ## [0.8.0](https://github.com/brick/math/releases/tag/0.8.0) - 2018-10-13 **Breaking changes** The following deprecated methods have been removed. Use the new method name instead: | Method removed | Replacement method | | --- | --- | | `BigDecimal::getIntegral()` | `BigDecimal::getIntegralPart()` | | `BigDecimal::getFraction()` | `BigDecimal::getFractionalPart()` | --- **New features** `BigInteger` has been augmented with 5 new methods for bitwise operations: | New method | Description | | --- | --- | | `and()` | performs a bitwise `AND` operation on two numbers | | `or()` | performs a bitwise `OR` operation on two numbers | | `xor()` | performs a bitwise `XOR` operation on two numbers | | `shiftedLeft()` | returns the number shifted left by a number of bits | | `shiftedRight()` | returns the number shifted right by a number of bits | Thanks to @DASPRiD 👍 ## [0.7.3](https://github.com/brick/math/releases/tag/0.7.3) - 2018-08-20 **New method:** `BigDecimal::hasNonZeroFractionalPart()` **Renamed/deprecated methods:** - `BigDecimal::getIntegral()` has been renamed to `getIntegralPart()` and is now deprecated - `BigDecimal::getFraction()` has been renamed to `getFractionalPart()` and is now deprecated ## [0.7.2](https://github.com/brick/math/releases/tag/0.7.2) - 2018-07-21 **Performance update** `BigInteger::parse()` and `toBase()` now use GMP's built-in base conversion features when available. ## [0.7.1](https://github.com/brick/math/releases/tag/0.7.1) - 2018-03-01 This is a maintenance release, no code has been changed. - When installed with `--no-dev`, the autoloader does not autoload tests anymore - Tests and other files unnecessary for production are excluded from the dist package This will help make installations more compact. ## [0.7.0](https://github.com/brick/math/releases/tag/0.7.0) - 2017-10-02 Methods renamed: - `BigNumber:sign()` has been renamed to `getSign()` - `BigDecimal::unscaledValue()` has been renamed to `getUnscaledValue()` - `BigDecimal::scale()` has been renamed to `getScale()` - `BigDecimal::integral()` has been renamed to `getIntegral()` - `BigDecimal::fraction()` has been renamed to `getFraction()` - `BigRational::numerator()` has been renamed to `getNumerator()` - `BigRational::denominator()` has been renamed to `getDenominator()` Classes renamed: - `ArithmeticException` has been renamed to `MathException` ## [0.6.2](https://github.com/brick/math/releases/tag/0.6.2) - 2017-10-02 The base class for all exceptions is now `MathException`. `ArithmeticException` has been deprecated, and will be removed in 0.7.0. ## [0.6.1](https://github.com/brick/math/releases/tag/0.6.1) - 2017-10-02 A number of methods have been renamed: - `BigNumber:sign()` is deprecated; use `getSign()` instead - `BigDecimal::unscaledValue()` is deprecated; use `getUnscaledValue()` instead - `BigDecimal::scale()` is deprecated; use `getScale()` instead - `BigDecimal::integral()` is deprecated; use `getIntegral()` instead - `BigDecimal::fraction()` is deprecated; use `getFraction()` instead - `BigRational::numerator()` is deprecated; use `getNumerator()` instead - `BigRational::denominator()` is deprecated; use `getDenominator()` instead The old methods will be removed in version 0.7.0. ## [0.6.0](https://github.com/brick/math/releases/tag/0.6.0) - 2017-08-25 - Minimum PHP version is now [7.1](https://gophp71.org/); for PHP 5.6 and PHP 7.0 support, use version `0.5` - Deprecated method `BigDecimal::withScale()` has been removed; use `toScale()` instead - Method `BigNumber::toInteger()` has been renamed to `toInt()` ## [0.5.4](https://github.com/brick/math/releases/tag/0.5.4) - 2016-10-17 `BigNumber` classes now implement [JsonSerializable](http://php.net/manual/en/class.jsonserializable.php). The JSON output is always a string. ## [0.5.3](https://github.com/brick/math/releases/tag/0.5.3) - 2016-03-31 This is a bugfix release. Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6. ## [0.5.2](https://github.com/brick/math/releases/tag/0.5.2) - 2015-08-06 The `$scale` parameter of `BigDecimal::dividedBy()` is now optional again. ## [0.5.1](https://github.com/brick/math/releases/tag/0.5.1) - 2015-07-05 **New method: `BigNumber::toScale()`** This allows to convert any `BigNumber` to a `BigDecimal` with a given scale, using rounding if necessary. ## [0.5.0](https://github.com/brick/math/releases/tag/0.5.0) - 2015-07-04 **New features** - Common `BigNumber` interface for all classes, with the following methods: - `sign()` and derived methods (`isZero()`, `isPositive()`, ...) - `compareTo()` and derived methods (`isEqualTo()`, `isGreaterThan()`, ...) that work across different `BigNumber` types - `toBigInteger()`, `toBigDecimal()`, `toBigRational`() conversion methods - `toInteger()` and `toFloat()` conversion methods to native types - Unified `of()` behaviour: every class now accepts any type of number, provided that it can be safely converted to the current type - New method: `BigDecimal::exactlyDividedBy()`; this method automatically computes the scale of the result, provided that the division yields a finite number of digits - New methods: `BigRational::quotient()` and `remainder()` - Fine-grained exceptions: `DivisionByZeroException`, `RoundingNecessaryException`, `NumberFormatException` - Factory methods `zero()`, `one()` and `ten()` available in all classes - Rounding mode reintroduced in `BigInteger::dividedBy()` This release also comes with many performance improvements. --- **Breaking changes** - `BigInteger`: - `getSign()` is renamed to `sign()` - `toString()` is renamed to `toBase()` - `BigInteger::dividedBy()` now throws an exception by default if the remainder is not zero; use `quotient()` to get the previous behaviour - `BigDecimal`: - `getSign()` is renamed to `sign()` - `getUnscaledValue()` is renamed to `unscaledValue()` - `getScale()` is renamed to `scale()` - `getIntegral()` is renamed to `integral()` - `getFraction()` is renamed to `fraction()` - `divideAndRemainder()` is renamed to `quotientAndRemainder()` - `dividedBy()` now takes a **mandatory** `$scale` parameter **before** the rounding mode - `toBigInteger()` does not accept a `$roundingMode` parameter anymore - `toBigRational()` does not simplify the fraction anymore; explicitly add `->simplified()` to get the previous behaviour - `BigRational`: - `getSign()` is renamed to `sign()` - `getNumerator()` is renamed to `numerator()` - `getDenominator()` is renamed to `denominator()` - `of()` is renamed to `nd()`, while `parse()` is renamed to `of()` - Miscellaneous: - `ArithmeticException` is moved to an `Exception\` sub-namespace - `of()` factory methods now throw `NumberFormatException` instead of `InvalidArgumentException` ## [0.4.3](https://github.com/brick/math/releases/tag/0.4.3) - 2016-03-31 Backport of two bug fixes from the 0.5 branch: - `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected - Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6. ## [0.4.2](https://github.com/brick/math/releases/tag/0.4.2) - 2015-06-16 New method: `BigDecimal::stripTrailingZeros()` ## [0.4.1](https://github.com/brick/math/releases/tag/0.4.1) - 2015-06-12 Introducing a `BigRational` class, to perform calculations on fractions of any size. ## [0.4.0](https://github.com/brick/math/releases/tag/0.4.0) - 2015-06-12 Rounding modes have been removed from `BigInteger`, and are now a concept specific to `BigDecimal`. `BigInteger::dividedBy()` now always returns the quotient of the division. ## [0.3.5](https://github.com/brick/math/releases/tag/0.3.5) - 2016-03-31 Backport of two bug fixes from the 0.5 branch: - `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected - Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6. ## [0.3.4](https://github.com/brick/math/releases/tag/0.3.4) - 2015-06-11 New methods: - `BigInteger::remainder()` returns the remainder of a division only - `BigInteger::gcd()` returns the greatest common divisor of two numbers ## [0.3.3](https://github.com/brick/math/releases/tag/0.3.3) - 2015-06-07 Fix `toString()` not handling negative numbers. ## [0.3.2](https://github.com/brick/math/releases/tag/0.3.2) - 2015-06-07 `BigInteger` and `BigDecimal` now have a `getSign()` method that returns: - `-1` if the number is negative - `0` if the number is zero - `1` if the number is positive ## [0.3.1](https://github.com/brick/math/releases/tag/0.3.1) - 2015-06-05 Minor performance improvements ## [0.3.0](https://github.com/brick/math/releases/tag/0.3.0) - 2015-06-04 The `$roundingMode` and `$scale` parameters have been swapped in `BigDecimal::dividedBy()`. ## [0.2.2](https://github.com/brick/math/releases/tag/0.2.2) - 2015-06-04 Stronger immutability guarantee for `BigInteger` and `BigDecimal`. So far, it would have been possible to break immutability of these classes by calling the `unserialize()` internal function. This release fixes that. ## [0.2.1](https://github.com/brick/math/releases/tag/0.2.1) - 2015-06-02 Added `BigDecimal::divideAndRemainder()` ## [0.2.0](https://github.com/brick/math/releases/tag/0.2.0) - 2015-05-22 - `min()` and `max()` do not accept an `array` anymore, but a variable number of parameters - **minimum PHP version is now 5.6** - continuous integration with PHP 7 ## [0.1.1](https://github.com/brick/math/releases/tag/0.1.1) - 2014-09-01 - Added `BigInteger::power()` - Added HHVM support ## [0.1.0](https://github.com/brick/math/releases/tag/0.1.0) - 2014-08-31 First beta release. PK !>�ZE�� 2 2 math/src/BigRational.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math; use Brick\Math\Exception\DivisionByZeroException; use Brick\Math\Exception\MathException; use Brick\Math\Exception\NumberFormatException; use Brick\Math\Exception\RoundingNecessaryException; /** * An arbitrarily large rational number. * * This class is immutable. * * @psalm-immutable */ final class BigRational extends BigNumber { /** * The numerator. */ private BigInteger $numerator; /** * The denominator. Always strictly positive. */ private BigInteger $denominator; /** * Protected constructor. Use a factory method to obtain an instance. * * @param BigInteger $numerator The numerator. * @param BigInteger $denominator The denominator. * @param bool $checkDenominator Whether to check the denominator for negative and zero. * * @throws DivisionByZeroException If the denominator is zero. */ protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) { if ($checkDenominator) { if ($denominator->isZero()) { throw DivisionByZeroException::denominatorMustNotBeZero(); } if ($denominator->isNegative()) { $numerator = $numerator->negated(); $denominator = $denominator->negated(); } } $this->numerator = $numerator; $this->denominator = $denominator; } /** * Creates a BigRational of the given value. * * @throws MathException If the value cannot be converted to a BigRational. * * @psalm-pure */ public static function of(BigNumber|int|float|string $value) : BigRational { return parent::of($value)->toBigRational(); } /** * Creates a BigRational out of a numerator and a denominator. * * If the denominator is negative, the signs of both the numerator and the denominator * will be inverted to ensure that the denominator is always positive. * * @param BigNumber|int|float|string $numerator The numerator. Must be convertible to a BigInteger. * @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger. * * @throws NumberFormatException If an argument does not represent a valid number. * @throws RoundingNecessaryException If an argument represents a non-integer number. * @throws DivisionByZeroException If the denominator is zero. * * @psalm-pure */ public static function nd( BigNumber|int|float|string $numerator, BigNumber|int|float|string $denominator, ) : BigRational { $numerator = BigInteger::of($numerator); $denominator = BigInteger::of($denominator); return new BigRational($numerator, $denominator, true); } /** * Returns a BigRational representing zero. * * @psalm-pure */ public static function zero() : BigRational { /** * @psalm-suppress ImpureStaticVariable * @var BigRational|null $zero */ static $zero; if ($zero === null) { $zero = new BigRational(BigInteger::zero(), BigInteger::one(), false); } return $zero; } /** * Returns a BigRational representing one. * * @psalm-pure */ public static function one() : BigRational { /** * @psalm-suppress ImpureStaticVariable * @var BigRational|null $one */ static $one; if ($one === null) { $one = new BigRational(BigInteger::one(), BigInteger::one(), false); } return $one; } /** * Returns a BigRational representing ten. * * @psalm-pure */ public static function ten() : BigRational { /** * @psalm-suppress ImpureStaticVariable * @var BigRational|null $ten */ static $ten; if ($ten === null) { $ten = new BigRational(BigInteger::ten(), BigInteger::one(), false); } return $ten; } public function getNumerator() : BigInteger { return $this->numerator; } public function getDenominator() : BigInteger { return $this->denominator; } /** * Returns the quotient of the division of the numerator by the denominator. */ public function quotient() : BigInteger { return $this->numerator->quotient($this->denominator); } /** * Returns the remainder of the division of the numerator by the denominator. */ public function remainder() : BigInteger { return $this->numerator->remainder($this->denominator); } /** * Returns the quotient and remainder of the division of the numerator by the denominator. * * @return BigInteger[] */ public function quotientAndRemainder() : array { return $this->numerator->quotientAndRemainder($this->denominator); } /** * Returns the sum of this number and the given one. * * @param BigNumber|int|float|string $that The number to add. * * @throws MathException If the number is not valid. */ public function plus(BigNumber|int|float|string $that) : BigRational { $that = BigRational::of($that); $numerator = $this->numerator->multipliedBy($that->denominator); $numerator = $numerator->plus($that->numerator->multipliedBy($this->denominator)); $denominator = $this->denominator->multipliedBy($that->denominator); return new BigRational($numerator, $denominator, false); } /** * Returns the difference of this number and the given one. * * @param BigNumber|int|float|string $that The number to subtract. * * @throws MathException If the number is not valid. */ public function minus(BigNumber|int|float|string $that) : BigRational { $that = BigRational::of($that); $numerator = $this->numerator->multipliedBy($that->denominator); $numerator = $numerator->minus($that->numerator->multipliedBy($this->denominator)); $denominator = $this->denominator->multipliedBy($that->denominator); return new BigRational($numerator, $denominator, false); } /** * Returns the product of this number and the given one. * * @param BigNumber|int|float|string $that The multiplier. * * @throws MathException If the multiplier is not a valid number. */ public function multipliedBy(BigNumber|int|float|string $that) : BigRational { $that = BigRational::of($that); $numerator = $this->numerator->multipliedBy($that->numerator); $denominator = $this->denominator->multipliedBy($that->denominator); return new BigRational($numerator, $denominator, false); } /** * Returns the result of the division of this number by the given one. * * @param BigNumber|int|float|string $that The divisor. * * @throws MathException If the divisor is not a valid number, or is zero. */ public function dividedBy(BigNumber|int|float|string $that) : BigRational { $that = BigRational::of($that); $numerator = $this->numerator->multipliedBy($that->denominator); $denominator = $this->denominator->multipliedBy($that->numerator); return new BigRational($numerator, $denominator, true); } /** * Returns this number exponentiated to the given value. * * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000. */ public function power(int $exponent) : BigRational { if ($exponent === 0) { $one = BigInteger::one(); return new BigRational($one, $one, false); } if ($exponent === 1) { return $this; } return new BigRational( $this->numerator->power($exponent), $this->denominator->power($exponent), false ); } /** * Returns the reciprocal of this BigRational. * * The reciprocal has the numerator and denominator swapped. * * @throws DivisionByZeroException If the numerator is zero. */ public function reciprocal() : BigRational { return new BigRational($this->denominator, $this->numerator, true); } /** * Returns the absolute value of this BigRational. */ public function abs() : BigRational { return new BigRational($this->numerator->abs(), $this->denominator, false); } /** * Returns the negated value of this BigRational. */ public function negated() : BigRational { return new BigRational($this->numerator->negated(), $this->denominator, false); } /** * Returns the simplified value of this BigRational. */ public function simplified() : BigRational { $gcd = $this->numerator->gcd($this->denominator); $numerator = $this->numerator->quotient($gcd); $denominator = $this->denominator->quotient($gcd); return new BigRational($numerator, $denominator, false); } public function compareTo(BigNumber|int|float|string $that) : int { return $this->minus($that)->getSign(); } public function getSign() : int { return $this->numerator->getSign(); } public function toBigInteger() : BigInteger { $simplified = $this->simplified(); if (! $simplified->denominator->isEqualTo(1)) { throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.'); } return $simplified->numerator; } public function toBigDecimal() : BigDecimal { return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator); } public function toBigRational() : BigRational { return $this; } public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal { return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode); } public function toInt() : int { return $this->toBigInteger()->toInt(); } public function toFloat() : float { $simplified = $this->simplified(); return $simplified->numerator->toFloat() / $simplified->denominator->toFloat(); } public function __toString() : string { $numerator = (string) $this->numerator; $denominator = (string) $this->denominator; if ($denominator === '1') { return $numerator; } return $this->numerator . '/' . $this->denominator; } /** * This method is required for serializing the object and SHOULD NOT be accessed directly. * * @internal * * @return array{numerator: BigInteger, denominator: BigInteger} */ public function __serialize(): array { return ['numerator' => $this->numerator, 'denominator' => $this->denominator]; } /** * This method is only here to allow unserializing the object and cannot be accessed directly. * * @internal * @psalm-suppress RedundantPropertyInitializationCheck * * @param array{numerator: BigInteger, denominator: BigInteger} $data * * @throws \LogicException */ public function __unserialize(array $data): void { if (isset($this->numerator)) { throw new \LogicException('__unserialize() is an internal function, it must not be called directly.'); } $this->numerator = $data['numerator']; $this->denominator = $data['denominator']; } /** * This method is required by interface Serializable and SHOULD NOT be accessed directly. * * @internal */ public function serialize() : string { return $this->numerator . '/' . $this->denominator; } /** * This method is only here to implement interface Serializable and cannot be accessed directly. * * @internal * @psalm-suppress RedundantPropertyInitializationCheck * * @throws \LogicException */ public function unserialize($value) : void { if (isset($this->numerator)) { throw new \LogicException('unserialize() is an internal function, it must not be called directly.'); } [$numerator, $denominator] = \explode('/', $value); $this->numerator = BigInteger::of($numerator); $this->denominator = BigInteger::of($denominator); } } PK !>�Z�S� math/src/RoundingMode.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math; /** * Specifies a rounding behavior for numerical operations capable of discarding precision. * * Each rounding mode indicates how the least significant returned digit of a rounded result * is to be calculated. If fewer digits are returned than the digits needed to represent the * exact numerical result, the discarded digits will be referred to as the discarded fraction * regardless the digits' contribution to the value of the number. In other words, considered * as a numerical value, the discarded fraction could have an absolute value greater than one. */ final class RoundingMode { /** * Private constructor. This class is not instantiable. * * @codeCoverageIgnore */ private function __construct() { } /** * Asserts that the requested operation has an exact result, hence no rounding is necessary. * * If this rounding mode is specified on an operation that yields a result that * cannot be represented at the requested scale, a RoundingNecessaryException is thrown. */ public const UNNECESSARY = 0; /** * Rounds away from zero. * * Always increments the digit prior to a nonzero discarded fraction. * Note that this rounding mode never decreases the magnitude of the calculated value. */ public const UP = 1; /** * Rounds towards zero. * * Never increments the digit prior to a discarded fraction (i.e., truncates). * Note that this rounding mode never increases the magnitude of the calculated value. */ public const DOWN = 2; /** * Rounds towards positive infinity. * * If the result is positive, behaves as for UP; if negative, behaves as for DOWN. * Note that this rounding mode never decreases the calculated value. */ public const CEILING = 3; /** * Rounds towards negative infinity. * * If the result is positive, behave as for DOWN; if negative, behave as for UP. * Note that this rounding mode never increases the calculated value. */ public const FLOOR = 4; /** * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up. * * Behaves as for UP if the discarded fraction is >= 0.5; otherwise, behaves as for DOWN. * Note that this is the rounding mode commonly taught at school. */ public const HALF_UP = 5; /** * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down. * * Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN. */ public const HALF_DOWN = 6; /** * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity. * * If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN. */ public const HALF_CEILING = 7; /** * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity. * * If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP. */ public const HALF_FLOOR = 8; /** * Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor. * * Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd; * behaves as for HALF_DOWN if it's even. * * Note that this is the rounding mode that statistically minimizes * cumulative error when applied repeatedly over a sequence of calculations. * It is sometimes known as "Banker's rounding", and is chiefly used in the USA. */ public const HALF_EVEN = 9; } PK !>�Z[��H� � 1 math/src/Internal/Calculator/BcMathCalculator.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math\Internal\Calculator; use Brick\Math\Internal\Calculator; /** * Calculator implementation built around the bcmath library. * * @internal * * @psalm-immutable */ class BcMathCalculator extends Calculator { public function add(string $a, string $b) : string { return \bcadd($a, $b, 0); } public function sub(string $a, string $b) : string { return \bcsub($a, $b, 0); } public function mul(string $a, string $b) : string { return \bcmul($a, $b, 0); } public function divQ(string $a, string $b) : string { return \bcdiv($a, $b, 0); } /** * @psalm-suppress InvalidNullableReturnType * @psalm-suppress NullableReturnStatement */ public function divR(string $a, string $b) : string { return \bcmod($a, $b, 0); } public function divQR(string $a, string $b) : array { $q = \bcdiv($a, $b, 0); $r = \bcmod($a, $b, 0); assert($r !== null); return [$q, $r]; } public function pow(string $a, int $e) : string { return \bcpow($a, (string) $e, 0); } public function modPow(string $base, string $exp, string $mod) : string { return \bcpowmod($base, $exp, $mod, 0); } /** * @psalm-suppress InvalidNullableReturnType * @psalm-suppress NullableReturnStatement */ public function sqrt(string $n) : string { return \bcsqrt($n, 0); } } PK !>�Z滭r�5 �5 1 math/src/Internal/Calculator/NativeCalculator.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math\Internal\Calculator; use Brick\Math\Internal\Calculator; /** * Calculator implementation using only native PHP code. * * @internal * * @psalm-immutable */ class NativeCalculator extends Calculator { /** * The max number of digits the platform can natively add, subtract, multiply or divide without overflow. * For multiplication, this represents the max sum of the lengths of both operands. * * In addition, it is assumed that an extra digit can hold a carry (1) without overflowing. * Example: 32-bit: max number 1,999,999,999 (9 digits + carry) * 64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry) */ private int $maxDigits; /** * @codeCoverageIgnore */ public function __construct() { switch (PHP_INT_SIZE) { case 4: $this->maxDigits = 9; break; case 8: $this->maxDigits = 18; break; default: throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.'); } } public function add(string $a, string $b) : string { /** * @psalm-var numeric-string $a * @psalm-var numeric-string $b */ $result = $a + $b; if (is_int($result)) { return (string) $result; } if ($a === '0') { return $b; } if ($b === '0') { return $a; } [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); $result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig); if ($aNeg) { $result = $this->neg($result); } return $result; } public function sub(string $a, string $b) : string { return $this->add($a, $this->neg($b)); } public function mul(string $a, string $b) : string { /** * @psalm-var numeric-string $a * @psalm-var numeric-string $b */ $result = $a * $b; if (is_int($result)) { return (string) $result; } if ($a === '0' || $b === '0') { return '0'; } if ($a === '1') { return $b; } if ($b === '1') { return $a; } if ($a === '-1') { return $this->neg($b); } if ($b === '-1') { return $this->neg($a); } [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); $result = $this->doMul($aDig, $bDig); if ($aNeg !== $bNeg) { $result = $this->neg($result); } return $result; } public function divQ(string $a, string $b) : string { return $this->divQR($a, $b)[0]; } public function divR(string $a, string $b): string { return $this->divQR($a, $b)[1]; } public function divQR(string $a, string $b) : array { if ($a === '0') { return ['0', '0']; } if ($a === $b) { return ['1', '0']; } if ($b === '1') { return [$a, '0']; } if ($b === '-1') { return [$this->neg($a), '0']; } /** @psalm-var numeric-string $a */ $na = $a * 1; // cast to number if (is_int($na)) { /** @psalm-var numeric-string $b */ $nb = $b * 1; if (is_int($nb)) { // the only division that may overflow is PHP_INT_MIN / -1, // which cannot happen here as we've already handled a divisor of -1 above. $r = $na % $nb; $q = ($na - $r) / $nb; assert(is_int($q)); return [ (string) $q, (string) $r ]; } } [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); [$q, $r] = $this->doDiv($aDig, $bDig); if ($aNeg !== $bNeg) { $q = $this->neg($q); } if ($aNeg) { $r = $this->neg($r); } return [$q, $r]; } public function pow(string $a, int $e) : string { if ($e === 0) { return '1'; } if ($e === 1) { return $a; } $odd = $e % 2; $e -= $odd; $aa = $this->mul($a, $a); /** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */ $result = $this->pow($aa, $e / 2); if ($odd === 1) { $result = $this->mul($result, $a); } return $result; } /** * Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/ */ public function modPow(string $base, string $exp, string $mod) : string { // special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0) if ($base === '0' && $exp === '0' && $mod === '1') { return '0'; } // special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0) if ($exp === '0' && $mod === '1') { return '0'; } $x = $base; $res = '1'; // numbers are positive, so we can use remainder instead of modulo $x = $this->divR($x, $mod); while ($exp !== '0') { if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd $res = $this->divR($this->mul($res, $x), $mod); } $exp = $this->divQ($exp, '2'); $x = $this->divR($this->mul($x, $x), $mod); } return $res; } /** * Adapted from https://cp-algorithms.com/num_methods/roots_newton.html */ public function sqrt(string $n) : string { if ($n === '0') { return '0'; } // initial approximation $x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1); $decreased = false; for (;;) { $nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2'); if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) { break; } $decreased = $this->cmp($nx, $x) < 0; $x = $nx; } return $x; } /** * Performs the addition of two non-signed large integers. */ private function doAdd(string $a, string $b) : string { [$a, $b, $length] = $this->pad($a, $b); $carry = 0; $result = ''; for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) { $blockLength = $this->maxDigits; if ($i < 0) { $blockLength += $i; /** @psalm-suppress LoopInvalidation */ $i = 0; } /** @psalm-var numeric-string $blockA */ $blockA = \substr($a, $i, $blockLength); /** @psalm-var numeric-string $blockB */ $blockB = \substr($b, $i, $blockLength); $sum = (string) ($blockA + $blockB + $carry); $sumLength = \strlen($sum); if ($sumLength > $blockLength) { $sum = \substr($sum, 1); $carry = 1; } else { if ($sumLength < $blockLength) { $sum = \str_repeat('0', $blockLength - $sumLength) . $sum; } $carry = 0; } $result = $sum . $result; if ($i === 0) { break; } } if ($carry === 1) { $result = '1' . $result; } return $result; } /** * Performs the subtraction of two non-signed large integers. */ private function doSub(string $a, string $b) : string { if ($a === $b) { return '0'; } // Ensure that we always subtract to a positive result: biggest minus smallest. $cmp = $this->doCmp($a, $b); $invert = ($cmp === -1); if ($invert) { $c = $a; $a = $b; $b = $c; } [$a, $b, $length] = $this->pad($a, $b); $carry = 0; $result = ''; $complement = 10 ** $this->maxDigits; for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) { $blockLength = $this->maxDigits; if ($i < 0) { $blockLength += $i; /** @psalm-suppress LoopInvalidation */ $i = 0; } /** @psalm-var numeric-string $blockA */ $blockA = \substr($a, $i, $blockLength); /** @psalm-var numeric-string $blockB */ $blockB = \substr($b, $i, $blockLength); $sum = $blockA - $blockB - $carry; if ($sum < 0) { $sum += $complement; $carry = 1; } else { $carry = 0; } $sum = (string) $sum; $sumLength = \strlen($sum); if ($sumLength < $blockLength) { $sum = \str_repeat('0', $blockLength - $sumLength) . $sum; } $result = $sum . $result; if ($i === 0) { break; } } // Carry cannot be 1 when the loop ends, as a > b assert($carry === 0); $result = \ltrim($result, '0'); if ($invert) { $result = $this->neg($result); } return $result; } /** * Performs the multiplication of two non-signed large integers. */ private function doMul(string $a, string $b) : string { $x = \strlen($a); $y = \strlen($b); $maxDigits = \intdiv($this->maxDigits, 2); $complement = 10 ** $maxDigits; $result = '0'; for ($i = $x - $maxDigits;; $i -= $maxDigits) { $blockALength = $maxDigits; if ($i < 0) { $blockALength += $i; /** @psalm-suppress LoopInvalidation */ $i = 0; } $blockA = (int) \substr($a, $i, $blockALength); $line = ''; $carry = 0; for ($j = $y - $maxDigits;; $j -= $maxDigits) { $blockBLength = $maxDigits; if ($j < 0) { $blockBLength += $j; /** @psalm-suppress LoopInvalidation */ $j = 0; } $blockB = (int) \substr($b, $j, $blockBLength); $mul = $blockA * $blockB + $carry; $value = $mul % $complement; $carry = ($mul - $value) / $complement; $value = (string) $value; $value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT); $line = $value . $line; if ($j === 0) { break; } } if ($carry !== 0) { $line = $carry . $line; } $line = \ltrim($line, '0'); if ($line !== '') { $line .= \str_repeat('0', $x - $blockALength - $i); $result = $this->add($result, $line); } if ($i === 0) { break; } } return $result; } /** * Performs the division of two non-signed large integers. * * @return string[] The quotient and remainder. */ private function doDiv(string $a, string $b) : array { $cmp = $this->doCmp($a, $b); if ($cmp === -1) { return ['0', $a]; } $x = \strlen($a); $y = \strlen($b); // we now know that a >= b && x >= y $q = '0'; // quotient $r = $a; // remainder $z = $y; // focus length, always $y or $y+1 for (;;) { $focus = \substr($a, 0, $z); $cmp = $this->doCmp($focus, $b); if ($cmp === -1) { if ($z === $x) { // remainder < dividend break; } $z++; } $zeros = \str_repeat('0', $x - $z); $q = $this->add($q, '1' . $zeros); $a = $this->sub($a, $b . $zeros); $r = $a; if ($r === '0') { // remainder == 0 break; } $x = \strlen($a); if ($x < $y) { // remainder < dividend break; } $z = $y; } return [$q, $r]; } /** * Compares two non-signed large numbers. * * @return int [-1, 0, 1] */ private function doCmp(string $a, string $b) : int { $x = \strlen($a); $y = \strlen($b); $cmp = $x <=> $y; if ($cmp !== 0) { return $cmp; } return \strcmp($a, $b) <=> 0; // enforce [-1, 0, 1] } /** * Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length. * * The numbers must only consist of digits, without leading minus sign. * * @return array{string, string, int} */ private function pad(string $a, string $b) : array { $x = \strlen($a); $y = \strlen($b); if ($x > $y) { $b = \str_repeat('0', $x - $y) . $b; return [$a, $b, $x]; } if ($x < $y) { $a = \str_repeat('0', $y - $x) . $a; return [$a, $b, $y]; } return [$a, $b, $x]; } } PK !>�ZmK~L . math/src/Internal/Calculator/GmpCalculator.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math\Internal\Calculator; use Brick\Math\Internal\Calculator; /** * Calculator implementation built around the GMP library. * * @internal * * @psalm-immutable */ class GmpCalculator extends Calculator { public function add(string $a, string $b) : string { return \gmp_strval(\gmp_add($a, $b)); } public function sub(string $a, string $b) : string { return \gmp_strval(\gmp_sub($a, $b)); } public function mul(string $a, string $b) : string { return \gmp_strval(\gmp_mul($a, $b)); } public function divQ(string $a, string $b) : string { return \gmp_strval(\gmp_div_q($a, $b)); } public function divR(string $a, string $b) : string { return \gmp_strval(\gmp_div_r($a, $b)); } public function divQR(string $a, string $b) : array { [$q, $r] = \gmp_div_qr($a, $b); return [ \gmp_strval($q), \gmp_strval($r) ]; } public function pow(string $a, int $e) : string { return \gmp_strval(\gmp_pow($a, $e)); } public function modInverse(string $x, string $m) : ?string { $result = \gmp_invert($x, $m); if ($result === false) { return null; } return \gmp_strval($result); } public function modPow(string $base, string $exp, string $mod) : string { return \gmp_strval(\gmp_powm($base, $exp, $mod)); } public function gcd(string $a, string $b) : string { return \gmp_strval(\gmp_gcd($a, $b)); } public function fromBase(string $number, int $base) : string { return \gmp_strval(\gmp_init($number, $base)); } public function toBase(string $number, int $base) : string { return \gmp_strval($number, $base); } public function and(string $a, string $b) : string { return \gmp_strval(\gmp_and($a, $b)); } public function or(string $a, string $b) : string { return \gmp_strval(\gmp_or($a, $b)); } public function xor(string $a, string $b) : string { return \gmp_strval(\gmp_xor($a, $b)); } public function sqrt(string $n) : string { return \gmp_strval(\gmp_sqrt($n)); } } PK !>�Z��L��K �K math/src/Internal/Calculator.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math\Internal; use Brick\Math\Exception\RoundingNecessaryException; use Brick\Math\RoundingMode; /** * Performs basic operations on arbitrary size integers. * * Unless otherwise specified, all parameters must be validated as non-empty strings of digits, * without leading zero, and with an optional leading minus sign if the number is not zero. * * Any other parameter format will lead to undefined behaviour. * All methods must return strings respecting this format, unless specified otherwise. * * @internal * * @psalm-immutable */ abstract class Calculator { /** * The maximum exponent value allowed for the pow() method. */ public const MAX_POWER = 1000000; /** * The alphabet for converting from and to base 2 to 36, lowercase. */ public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'; /** * The Calculator instance in use. */ private static ?Calculator $instance = null; /** * Sets the Calculator instance to use. * * An instance is typically set only in unit tests: the autodetect is usually the best option. * * @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect. */ final public static function set(?Calculator $calculator) : void { self::$instance = $calculator; } /** * Returns the Calculator instance to use. * * If none has been explicitly set, the fastest available implementation will be returned. * * @psalm-pure * @psalm-suppress ImpureStaticProperty */ final public static function get() : Calculator { if (self::$instance === null) { /** @psalm-suppress ImpureMethodCall */ self::$instance = self::detect(); } return self::$instance; } /** * Returns the fastest available Calculator implementation. * * @codeCoverageIgnore */ private static function detect() : Calculator { if (\extension_loaded('gmp')) { return new Calculator\GmpCalculator(); } if (\extension_loaded('bcmath')) { return new Calculator\BcMathCalculator(); } return new Calculator\NativeCalculator(); } /** * Extracts the sign & digits of the operands. * * @return array{bool, bool, string, string} Whether $a and $b are negative, followed by their digits. */ final protected function init(string $a, string $b) : array { return [ $aNeg = ($a[0] === '-'), $bNeg = ($b[0] === '-'), $aNeg ? \substr($a, 1) : $a, $bNeg ? \substr($b, 1) : $b, ]; } /** * Returns the absolute value of a number. */ final public function abs(string $n) : string { return ($n[0] === '-') ? \substr($n, 1) : $n; } /** * Negates a number. */ final public function neg(string $n) : string { if ($n === '0') { return '0'; } if ($n[0] === '-') { return \substr($n, 1); } return '-' . $n; } /** * Compares two numbers. * * @return int [-1, 0, 1] If the first number is less than, equal to, or greater than the second number. */ final public function cmp(string $a, string $b) : int { [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); if ($aNeg && ! $bNeg) { return -1; } if ($bNeg && ! $aNeg) { return 1; } $aLen = \strlen($aDig); $bLen = \strlen($bDig); if ($aLen < $bLen) { $result = -1; } elseif ($aLen > $bLen) { $result = 1; } else { $result = $aDig <=> $bDig; } return $aNeg ? -$result : $result; } /** * Adds two numbers. */ abstract public function add(string $a, string $b) : string; /** * Subtracts two numbers. */ abstract public function sub(string $a, string $b) : string; /** * Multiplies two numbers. */ abstract public function mul(string $a, string $b) : string; /** * Returns the quotient of the division of two numbers. * * @param string $a The dividend. * @param string $b The divisor, must not be zero. * * @return string The quotient. */ abstract public function divQ(string $a, string $b) : string; /** * Returns the remainder of the division of two numbers. * * @param string $a The dividend. * @param string $b The divisor, must not be zero. * * @return string The remainder. */ abstract public function divR(string $a, string $b) : string; /** * Returns the quotient and remainder of the division of two numbers. * * @param string $a The dividend. * @param string $b The divisor, must not be zero. * * @return array{string, string} An array containing the quotient and remainder. */ abstract public function divQR(string $a, string $b) : array; /** * Exponentiates a number. * * @param string $a The base number. * @param int $e The exponent, validated as an integer between 0 and MAX_POWER. * * @return string The power. */ abstract public function pow(string $a, int $e) : string; /** * @param string $b The modulus; must not be zero. */ public function mod(string $a, string $b) : string { return $this->divR($this->add($this->divR($a, $b), $b), $b); } /** * Returns the modular multiplicative inverse of $x modulo $m. * * If $x has no multiplicative inverse mod m, this method must return null. * * This method can be overridden by the concrete implementation if the underlying library has built-in support. * * @param string $m The modulus; must not be negative or zero. */ public function modInverse(string $x, string $m) : ?string { if ($m === '1') { return '0'; } $modVal = $x; if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) { $modVal = $this->mod($x, $m); } [$g, $x] = $this->gcdExtended($modVal, $m); if ($g !== '1') { return null; } return $this->mod($this->add($this->mod($x, $m), $m), $m); } /** * Raises a number into power with modulo. * * @param string $base The base number; must be positive or zero. * @param string $exp The exponent; must be positive or zero. * @param string $mod The modulus; must be strictly positive. */ abstract public function modPow(string $base, string $exp, string $mod) : string; /** * Returns the greatest common divisor of the two numbers. * * This method can be overridden by the concrete implementation if the underlying library * has built-in support for GCD calculations. * * @return string The GCD, always positive, or zero if both arguments are zero. */ public function gcd(string $a, string $b) : string { if ($a === '0') { return $this->abs($b); } if ($b === '0') { return $this->abs($a); } return $this->gcd($b, $this->divR($a, $b)); } /** * @return array{string, string, string} GCD, X, Y */ private function gcdExtended(string $a, string $b) : array { if ($a === '0') { return [$b, '0', '1']; } [$gcd, $x1, $y1] = $this->gcdExtended($this->mod($b, $a), $a); $x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1)); $y = $x1; return [$gcd, $x, $y]; } /** * Returns the square root of the given number, rounded down. * * The result is the largest x such that x² ≤ n. * The input MUST NOT be negative. */ abstract public function sqrt(string $n) : string; /** * Converts a number from an arbitrary base. * * This method can be overridden by the concrete implementation if the underlying library * has built-in support for base conversion. * * @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base. * @param int $base The base of the number, validated from 2 to 36. * * @return string The converted number, following the Calculator conventions. */ public function fromBase(string $number, int $base) : string { return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base); } /** * Converts a number to an arbitrary base. * * This method can be overridden by the concrete implementation if the underlying library * has built-in support for base conversion. * * @param string $number The number to convert, following the Calculator conventions. * @param int $base The base to convert to, validated from 2 to 36. * * @return string The converted number, lowercase. */ public function toBase(string $number, int $base) : string { $negative = ($number[0] === '-'); if ($negative) { $number = \substr($number, 1); } $number = $this->toArbitraryBase($number, self::ALPHABET, $base); if ($negative) { return '-' . $number; } return $number; } /** * Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10. * * @param string $number The number to convert, validated as a non-empty string, * containing only chars in the given alphabet/base. * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum. * @param int $base The base of the number, validated from 2 to alphabet length. * * @return string The number in base 10, following the Calculator conventions. */ final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string { // remove leading "zeros" $number = \ltrim($number, $alphabet[0]); if ($number === '') { return '0'; } // optimize for "one" if ($number === $alphabet[1]) { return '1'; } $result = '0'; $power = '1'; $base = (string) $base; for ($i = \strlen($number) - 1; $i >= 0; $i--) { $index = \strpos($alphabet, $number[$i]); if ($index !== 0) { $result = $this->add($result, ($index === 1) ? $power : $this->mul($power, (string) $index) ); } if ($i !== 0) { $power = $this->mul($power, $base); } } return $result; } /** * Converts a non-negative number to an arbitrary base using a custom alphabet. * * @param string $number The number to convert, positive or zero, following the Calculator conventions. * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum. * @param int $base The base to convert to, validated from 2 to alphabet length. * * @return string The converted number in the given alphabet. */ final public function toArbitraryBase(string $number, string $alphabet, int $base) : string { if ($number === '0') { return $alphabet[0]; } $base = (string) $base; $result = ''; while ($number !== '0') { [$number, $remainder] = $this->divQR($number, $base); $remainder = (int) $remainder; $result .= $alphabet[$remainder]; } return \strrev($result); } /** * Performs a rounded division. * * Rounding is performed when the remainder of the division is not zero. * * @param string $a The dividend. * @param string $b The divisor, must not be zero. * @param int $roundingMode The rounding mode. * * @throws \InvalidArgumentException If the rounding mode is invalid. * @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary. * * @psalm-suppress ImpureFunctionCall */ final public function divRound(string $a, string $b, int $roundingMode) : string { [$quotient, $remainder] = $this->divQR($a, $b); $hasDiscardedFraction = ($remainder !== '0'); $isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-'); $discardedFractionSign = function() use ($remainder, $b) : int { $r = $this->abs($this->mul($remainder, '2')); $b = $this->abs($b); return $this->cmp($r, $b); }; $increment = false; switch ($roundingMode) { case RoundingMode::UNNECESSARY: if ($hasDiscardedFraction) { throw RoundingNecessaryException::roundingNecessary(); } break; case RoundingMode::UP: $increment = $hasDiscardedFraction; break; case RoundingMode::DOWN: break; case RoundingMode::CEILING: $increment = $hasDiscardedFraction && $isPositiveOrZero; break; case RoundingMode::FLOOR: $increment = $hasDiscardedFraction && ! $isPositiveOrZero; break; case RoundingMode::HALF_UP: $increment = $discardedFractionSign() >= 0; break; case RoundingMode::HALF_DOWN: $increment = $discardedFractionSign() > 0; break; case RoundingMode::HALF_CEILING: $increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0; break; case RoundingMode::HALF_FLOOR: $increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0; break; case RoundingMode::HALF_EVEN: $lastDigit = (int) $quotient[-1]; $lastDigitIsEven = ($lastDigit % 2 === 0); $increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0; break; default: throw new \InvalidArgumentException('Invalid rounding mode.'); } if ($increment) { return $this->add($quotient, $isPositiveOrZero ? '1' : '-1'); } return $quotient; } /** * Calculates bitwise AND of two numbers. * * This method can be overridden by the concrete implementation if the underlying library * has built-in support for bitwise operations. */ public function and(string $a, string $b) : string { return $this->bitwise('and', $a, $b); } /** * Calculates bitwise OR of two numbers. * * This method can be overridden by the concrete implementation if the underlying library * has built-in support for bitwise operations. */ public function or(string $a, string $b) : string { return $this->bitwise('or', $a, $b); } /** * Calculates bitwise XOR of two numbers. * * This method can be overridden by the concrete implementation if the underlying library * has built-in support for bitwise operations. */ public function xor(string $a, string $b) : string { return $this->bitwise('xor', $a, $b); } /** * Performs a bitwise operation on a decimal number. * * @param 'and'|'or'|'xor' $operator The operator to use. * @param string $a The left operand. * @param string $b The right operand. */ private function bitwise(string $operator, string $a, string $b) : string { [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); $aBin = $this->toBinary($aDig); $bBin = $this->toBinary($bDig); $aLen = \strlen($aBin); $bLen = \strlen($bBin); if ($aLen > $bLen) { $bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin; } elseif ($bLen > $aLen) { $aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin; } if ($aNeg) { $aBin = $this->twosComplement($aBin); } if ($bNeg) { $bBin = $this->twosComplement($bBin); } switch ($operator) { case 'and': $value = $aBin & $bBin; $negative = ($aNeg and $bNeg); break; case 'or': $value = $aBin | $bBin; $negative = ($aNeg or $bNeg); break; case 'xor': $value = $aBin ^ $bBin; $negative = ($aNeg xor $bNeg); break; // @codeCoverageIgnoreStart default: throw new \InvalidArgumentException('Invalid bitwise operator.'); // @codeCoverageIgnoreEnd } if ($negative) { $value = $this->twosComplement($value); } $result = $this->toDecimal($value); return $negative ? $this->neg($result) : $result; } /** * @param string $number A positive, binary number. */ private function twosComplement(string $number) : string { $xor = \str_repeat("\xff", \strlen($number)); $number ^= $xor; for ($i = \strlen($number) - 1; $i >= 0; $i--) { $byte = \ord($number[$i]); if (++$byte !== 256) { $number[$i] = \chr($byte); break; } $number[$i] = "\x00"; if ($i === 0) { $number = "\x01" . $number; } } return $number; } /** * Converts a decimal number to a binary string. * * @param string $number The number to convert, positive or zero, only digits. */ private function toBinary(string $number) : string { $result = ''; while ($number !== '0') { [$number, $remainder] = $this->divQR($number, '256'); $result .= \chr((int) $remainder); } return \strrev($result); } /** * Returns the positive decimal representation of a binary number. * * @param string $bytes The bytes representing the number. */ private function toDecimal(string $bytes) : string { $result = '0'; $power = '1'; for ($i = \strlen($bytes) - 1; $i >= 0; $i--) { $index = \ord($bytes[$i]); if ($index !== 0) { $result = $this->add($result, ($index === 1) ? $power : $this->mul($power, (string) $index) ); } if ($i !== 0) { $power = $this->mul($power, '256'); } } return $result; } } PK !>�ZY��� � . math/src/Exception/NegativeNumberException.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math\Exception; /** * Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number. */ class NegativeNumberException extends MathException { } PK !>�Z�V�~ / math/src/Exception/IntegerOverflowException.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math\Exception; use Brick\Math\BigInteger; /** * Exception thrown when an integer overflow occurs. */ class IntegerOverflowException extends MathException { /** * @psalm-pure */ public static function toIntOverflow(BigInteger $value) : IntegerOverflowException { $message = '%s is out of range %d to %d and cannot be represented as an integer.'; return new self(\sprintf($message, (string) $value, PHP_INT_MIN, PHP_INT_MAX)); } } PK !>�Z^�|"� � , math/src/Exception/NumberFormatException.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math\Exception; /** * Exception thrown when attempting to create a number from a string with an invalid format. */ class NumberFormatException extends MathException { /** * @param string $char The failing character. * * @psalm-pure */ public static function charNotInAlphabet(string $char) : self { $ord = \ord($char); if ($ord < 32 || $ord > 126) { $char = \strtoupper(\dechex($ord)); if ($ord < 10) { $char = '0' . $char; } } else { $char = '"' . $char . '"'; } return new self(sprintf('Char %s is not a valid character in the given alphabet.', $char)); } } PK !>�Z1�>� � 1 math/src/Exception/RoundingNecessaryException.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math\Exception; /** * Exception thrown when a number cannot be represented at the requested scale without rounding. */ class RoundingNecessaryException extends MathException { /** * @psalm-pure */ public static function roundingNecessary() : RoundingNecessaryException { return new self('Rounding is necessary to represent the result of the operation at this scale.'); } } PK !>�Z�*� � $ math/src/Exception/MathException.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math\Exception; /** * Base class for all math exceptions. */ class MathException extends \Exception { } PK !>�Zə�b� � . math/src/Exception/DivisionByZeroException.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math\Exception; /** * Exception thrown when a division by zero occurs. */ class DivisionByZeroException extends MathException { /** * @psalm-pure */ public static function divisionByZero() : DivisionByZeroException { return new self('Division by zero.'); } /** * @psalm-pure */ public static function modulusMustNotBeZero() : DivisionByZeroException { return new self('The modulus must not be zero.'); } /** * @psalm-pure */ public static function denominatorMustNotBeZero() : DivisionByZeroException { return new self('The denominator of a rational number cannot be zero.'); } } PK !>�Z�\��� � math/src/BigInteger.phpnu �[��� <?php declare(strict_types=1); namespace Brick\Math; use Brick\Math\Exception\DivisionByZeroException; use Brick\Math\Exception\IntegerOverflowException; use Brick\Math\Exception\MathException; use Brick\Math\Exception\NegativeNumberException; use Brick\Math\Exception\NumberFormatException; use Brick\Math\Internal\Calculator; /** * An arbitrary-size integer. * * All methods accepting a number as a parameter accept either a BigInteger instance, * an integer, or a string representing an arbitrary size integer. * * @psalm-immutable */ final class BigInteger extends BigNumber { /** * The value, as a string of digits with optional leading minus sign. * * No leading zeros must be present. * No leading minus sign must be present if the number is zero. */ private string $value; /** * Protected constructor. Use a factory method to obtain an instance. * * @param string $value A string of digits, with optional leading minus sign. */ protected function __construct(string $value) { $this->value = $value; } /** * Creates a BigInteger of the given value. * * @throws MathException If the value cannot be converted to a BigInteger. * * @psalm-pure */ public static function of(BigNumber|int|float|string $value) : BigInteger { return parent::of($value)->toBigInteger(); } /** * Creates a number from a string in a given base. * * The string can optionally be prefixed with the `+` or `-` sign. * * Bases greater than 36 are not supported by this method, as there is no clear consensus on which of the lowercase * or uppercase characters should come first. Instead, this method accepts any base up to 36, and does not * differentiate lowercase and uppercase characters, which are considered equal. * * For bases greater than 36, and/or custom alphabets, use the fromArbitraryBase() method. * * @param string $number The number to convert, in the given base. * @param int $base The base of the number, between 2 and 36. * * @throws NumberFormatException If the number is empty, or contains invalid chars for the given base. * @throws \InvalidArgumentException If the base is out of range. * * @psalm-pure */ public static function fromBase(string $number, int $base) : BigInteger { if ($number === '') { throw new NumberFormatException('The number cannot be empty.'); } if ($base < 2 || $base > 36) { throw new \InvalidArgumentException(\sprintf('Base %d is not in range 2 to 36.', $base)); } if ($number[0] === '-') { $sign = '-'; $number = \substr($number, 1); } elseif ($number[0] === '+') { $sign = ''; $number = \substr($number, 1); } else { $sign = ''; } if ($number === '') { throw new NumberFormatException('The number cannot be empty.'); } $number = \ltrim($number, '0'); if ($number === '') { // The result will be the same in any base, avoid further calculation. return BigInteger::zero(); } if ($number === '1') { // The result will be the same in any base, avoid further calculation. return new BigInteger($sign . '1'); } $pattern = '/[^' . \substr(Calculator::ALPHABET, 0, $base) . ']/'; if (\preg_match($pattern, \strtolower($number), $matches) === 1) { throw new NumberFormatException(\sprintf('"%s" is not a valid character in base %d.', $matches[0], $base)); } if ($base === 10) { // The number is usable as is, avoid further calculation. return new BigInteger($sign . $number); } $result = Calculator::get()->fromBase($number, $base); return new BigInteger($sign . $result); } /** * Parses a string containing an integer in an arbitrary base, using a custom alphabet. * * Because this method accepts an alphabet with any character, including dash, it does not handle negative numbers. * * @param string $number The number to parse. * @param string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8. * * @throws NumberFormatException If the given number is empty or contains invalid chars for the given alphabet. * @throws \InvalidArgumentException If the alphabet does not contain at least 2 chars. * * @psalm-pure */ public static function fromArbitraryBase(string $number, string $alphabet) : BigInteger { if ($number === '') { throw new NumberFormatException('The number cannot be empty.'); } $base = \strlen($alphabet); if ($base < 2) { throw new \InvalidArgumentException('The alphabet must contain at least 2 chars.'); } $pattern = '/[^' . \preg_quote($alphabet, '/') . ']/'; if (\preg_match($pattern, $number, $matches) === 1) { throw NumberFormatException::charNotInAlphabet($matches[0]); } $number = Calculator::get()->fromArbitraryBase($number, $alphabet, $base); return new BigInteger($number); } /** * Translates a string of bytes containing the binary representation of a BigInteger into a BigInteger. * * The input string is assumed to be in big-endian byte-order: the most significant byte is in the zeroth element. * * If `$signed` is true, the input is assumed to be in two's-complement representation, and the leading bit is * interpreted as a sign bit. If `$signed` is false, the input is interpreted as an unsigned number, and the * resulting BigInteger will always be positive or zero. * * This method can be used to retrieve a number exported by `toBytes()`, as long as the `$signed` flags match. * * @param string $value The byte string. * @param bool $signed Whether to interpret as a signed number in two's-complement representation with a leading * sign bit. * * @throws NumberFormatException If the string is empty. */ public static function fromBytes(string $value, bool $signed = true) : BigInteger { if ($value === '') { throw new NumberFormatException('The byte string must not be empty.'); } $twosComplement = false; if ($signed) { $x = \ord($value[0]); if (($twosComplement = ($x >= 0x80))) { $value = ~$value; } } $number = self::fromBase(\bin2hex($value), 16); if ($twosComplement) { return $number->plus(1)->negated(); } return $number; } /** * Generates a pseudo-random number in the range 0 to 2^numBits - 1. * * Using the default random bytes generator, this method is suitable for cryptographic use. * * @psalm-param (callable(int): string)|null $randomBytesGenerator * * @param int $numBits The number of bits. * @param callable|null $randomBytesGenerator A function that accepts a number of bytes as an integer, and returns a * string of random bytes of the given length. Defaults to the * `random_bytes()` function. * * @throws \InvalidArgumentException If $numBits is negative. */ public static function randomBits(int $numBits, ?callable $randomBytesGenerator = null) : BigInteger { if ($numBits < 0) { throw new \InvalidArgumentException('The number of bits cannot be negative.'); } if ($numBits === 0) { return BigInteger::zero(); } if ($randomBytesGenerator === null) { $randomBytesGenerator = 'random_bytes'; } $byteLength = \intdiv($numBits - 1, 8) + 1; $extraBits = ($byteLength * 8 - $numBits); $bitmask = \chr(0xFF >> $extraBits); $randomBytes = $randomBytesGenerator($byteLength); $randomBytes[0] = $randomBytes[0] & $bitmask; return self::fromBytes($randomBytes, false); } /** * Generates a pseudo-random number between `$min` and `$max`. * * Using the default random bytes generator, this method is suitable for cryptographic use. * * @psalm-param (callable(int): string)|null $randomBytesGenerator * * @param BigNumber|int|float|string $min The lower bound. Must be convertible to a BigInteger. * @param BigNumber|int|float|string $max The upper bound. Must be convertible to a BigInteger. * @param callable|null $randomBytesGenerator A function that accepts a number of bytes as an integer, * and returns a string of random bytes of the given length. * Defaults to the `random_bytes()` function. * * @throws MathException If one of the parameters cannot be converted to a BigInteger, * or `$min` is greater than `$max`. */ public static function randomRange( BigNumber|int|float|string $min, BigNumber|int|float|string $max, ?callable $randomBytesGenerator = null ) : BigInteger { $min = BigInteger::of($min); $max = BigInteger::of($max); if ($min->isGreaterThan($max)) { throw new MathException('$min cannot be greater than $max.'); } if ($min->isEqualTo($max)) { return $min; } $diff = $max->minus($min); $bitLength = $diff->getBitLength(); // try until the number is in range (50% to 100% chance of success) do { $randomNumber = self::randomBits($bitLength, $randomBytesGenerator); } while ($randomNumber->isGreaterThan($diff)); return $randomNumber->plus($min); } /** * Returns a BigInteger representing zero. * * @psalm-pure */ public static function zero() : BigInteger { /** * @psalm-suppress ImpureStaticVariable * @var BigInteger|null $zero */ static $zero; if ($zero === null) { $zero = new BigInteger('0'); } return $zero; } /** * Returns a BigInteger representing one. * * @psalm-pure */ public static function one() : BigInteger { /** * @psalm-suppress ImpureStaticVariable * @var BigInteger|null $one */ static $one; if ($one === null) { $one = new BigInteger('1'); } return $one; } /** * Returns a BigInteger representing ten. * * @psalm-pure */ public static function ten() : BigInteger { /** * @psalm-suppress ImpureStaticVariable * @var BigInteger|null $ten */ static $ten; if ($ten === null) { $ten = new BigInteger('10'); } return $ten; } public static function gcdMultiple(BigInteger $a, BigInteger ...$n): BigInteger { $result = $a; foreach ($n as $next) { $result = $result->gcd($next); if ($result->isEqualTo(1)) { return $result; } } return $result; } /** * Returns the sum of this number and the given one. * * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigInteger. * * @throws MathException If the number is not valid, or is not convertible to a BigInteger. */ public function plus(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); if ($that->value === '0') { return $this; } if ($this->value === '0') { return $that; } $value = Calculator::get()->add($this->value, $that->value); return new BigInteger($value); } /** * Returns the difference of this number and the given one. * * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigInteger. * * @throws MathException If the number is not valid, or is not convertible to a BigInteger. */ public function minus(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); if ($that->value === '0') { return $this; } $value = Calculator::get()->sub($this->value, $that->value); return new BigInteger($value); } /** * Returns the product of this number and the given one. * * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigInteger. * * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigInteger. */ public function multipliedBy(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); if ($that->value === '1') { return $this; } if ($this->value === '1') { return $that; } $value = Calculator::get()->mul($this->value, $that->value); return new BigInteger($value); } /** * Returns the result of the division of this number by the given one. * * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. * @param int $roundingMode An optional rounding mode. * * @throws MathException If the divisor is not a valid number, is not convertible to a BigInteger, is zero, * or RoundingMode::UNNECESSARY is used and the remainder is not zero. */ public function dividedBy(BigNumber|int|float|string $that, int $roundingMode = RoundingMode::UNNECESSARY) : BigInteger { $that = BigInteger::of($that); if ($that->value === '1') { return $this; } if ($that->value === '0') { throw DivisionByZeroException::divisionByZero(); } $result = Calculator::get()->divRound($this->value, $that->value, $roundingMode); return new BigInteger($result); } /** * Returns this number exponentiated to the given value. * * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000. */ public function power(int $exponent) : BigInteger { if ($exponent === 0) { return BigInteger::one(); } if ($exponent === 1) { return $this; } if ($exponent < 0 || $exponent > Calculator::MAX_POWER) { throw new \InvalidArgumentException(\sprintf( 'The exponent %d is not in the range 0 to %d.', $exponent, Calculator::MAX_POWER )); } return new BigInteger(Calculator::get()->pow($this->value, $exponent)); } /** * Returns the quotient of the division of this number by the given one. * * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. * * @throws DivisionByZeroException If the divisor is zero. */ public function quotient(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); if ($that->value === '1') { return $this; } if ($that->value === '0') { throw DivisionByZeroException::divisionByZero(); } $quotient = Calculator::get()->divQ($this->value, $that->value); return new BigInteger($quotient); } /** * Returns the remainder of the division of this number by the given one. * * The remainder, when non-zero, has the same sign as the dividend. * * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. * * @throws DivisionByZeroException If the divisor is zero. */ public function remainder(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); if ($that->value === '1') { return BigInteger::zero(); } if ($that->value === '0') { throw DivisionByZeroException::divisionByZero(); } $remainder = Calculator::get()->divR($this->value, $that->value); return new BigInteger($remainder); } /** * Returns the quotient and remainder of the division of this number by the given one. * * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. * * @return BigInteger[] An array containing the quotient and the remainder. * * @throws DivisionByZeroException If the divisor is zero. */ public function quotientAndRemainder(BigNumber|int|float|string $that) : array { $that = BigInteger::of($that); if ($that->value === '0') { throw DivisionByZeroException::divisionByZero(); } [$quotient, $remainder] = Calculator::get()->divQR($this->value, $that->value); return [ new BigInteger($quotient), new BigInteger($remainder) ]; } /** * Returns the modulo of this number and the given one. * * The modulo operation yields the same result as the remainder operation when both operands are of the same sign, * and may differ when signs are different. * * The result of the modulo operation, when non-zero, has the same sign as the divisor. * * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. * * @throws DivisionByZeroException If the divisor is zero. */ public function mod(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); if ($that->value === '0') { throw DivisionByZeroException::modulusMustNotBeZero(); } $value = Calculator::get()->mod($this->value, $that->value); return new BigInteger($value); } /** * Returns the modular multiplicative inverse of this BigInteger modulo $m. * * @throws DivisionByZeroException If $m is zero. * @throws NegativeNumberException If $m is negative. * @throws MathException If this BigInteger has no multiplicative inverse mod m (that is, this BigInteger * is not relatively prime to m). */ public function modInverse(BigInteger $m) : BigInteger { if ($m->value === '0') { throw DivisionByZeroException::modulusMustNotBeZero(); } if ($m->isNegative()) { throw new NegativeNumberException('Modulus must not be negative.'); } if ($m->value === '1') { return BigInteger::zero(); } $value = Calculator::get()->modInverse($this->value, $m->value); if ($value === null) { throw new MathException('Unable to compute the modInverse for the given modulus.'); } return new BigInteger($value); } /** * Returns this number raised into power with modulo. * * This operation only works on positive numbers. * * @param BigNumber|int|float|string $exp The exponent. Must be positive or zero. * @param BigNumber|int|float|string $mod The modulus. Must be strictly positive. * * @throws NegativeNumberException If any of the operands is negative. * @throws DivisionByZeroException If the modulus is zero. */ public function modPow(BigNumber|int|float|string $exp, BigNumber|int|float|string $mod) : BigInteger { $exp = BigInteger::of($exp); $mod = BigInteger::of($mod); if ($this->isNegative() || $exp->isNegative() || $mod->isNegative()) { throw new NegativeNumberException('The operands cannot be negative.'); } if ($mod->isZero()) { throw DivisionByZeroException::modulusMustNotBeZero(); } $result = Calculator::get()->modPow($this->value, $exp->value, $mod->value); return new BigInteger($result); } /** * Returns the greatest common divisor of this number and the given one. * * The GCD is always positive, unless both operands are zero, in which case it is zero. * * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. */ public function gcd(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); if ($that->value === '0' && $this->value[0] !== '-') { return $this; } if ($this->value === '0' && $that->value[0] !== '-') { return $that; } $value = Calculator::get()->gcd($this->value, $that->value); return new BigInteger($value); } /** * Returns the integer square root number of this number, rounded down. * * The result is the largest x such that x² ≤ n. * * @throws NegativeNumberException If this number is negative. */ public function sqrt() : BigInteger { if ($this->value[0] === '-') { throw new NegativeNumberException('Cannot calculate the square root of a negative number.'); } $value = Calculator::get()->sqrt($this->value); return new BigInteger($value); } /** * Returns the absolute value of this number. */ public function abs() : BigInteger { return $this->isNegative() ? $this->negated() : $this; } /** * Returns the inverse of this number. */ public function negated() : BigInteger { return new BigInteger(Calculator::get()->neg($this->value)); } /** * Returns the integer bitwise-and combined with another integer. * * This method returns a negative BigInteger if and only if both operands are negative. * * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. */ public function and(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); return new BigInteger(Calculator::get()->and($this->value, $that->value)); } /** * Returns the integer bitwise-or combined with another integer. * * This method returns a negative BigInteger if and only if either of the operands is negative. * * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. */ public function or(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); return new BigInteger(Calculator::get()->or($this->value, $that->value)); } /** * Returns the integer bitwise-xor combined with another integer. * * This method returns a negative BigInteger if and only if exactly one of the operands is negative. * * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. */ public function xor(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); return new BigInteger(Calculator::get()->xor($this->value, $that->value)); } /** * Returns the bitwise-not of this BigInteger. */ public function not() : BigInteger { return $this->negated()->minus(1); } /** * Returns the integer left shifted by a given number of bits. */ public function shiftedLeft(int $distance) : BigInteger { if ($distance === 0) { return $this; } if ($distance < 0) { return $this->shiftedRight(- $distance); } return $this->multipliedBy(BigInteger::of(2)->power($distance)); } /** * Returns the integer right shifted by a given number of bits. */ public function shiftedRight(int $distance) : BigInteger { if ($distance === 0) { return $this; } if ($distance < 0) { return $this->shiftedLeft(- $distance); } $operand = BigInteger::of(2)->power($distance); if ($this->isPositiveOrZero()) { return $this->quotient($operand); } return $this->dividedBy($operand, RoundingMode::UP); } /** * Returns the number of bits in the minimal two's-complement representation of this BigInteger, excluding a sign bit. * * For positive BigIntegers, this is equivalent to the number of bits in the ordinary binary representation. * Computes (ceil(log2(this < 0 ? -this : this+1))). */ public function getBitLength() : int { if ($this->value === '0') { return 0; } if ($this->isNegative()) { return $this->abs()->minus(1)->getBitLength(); } return \strlen($this->toBase(2)); } /** * Returns the index of the rightmost (lowest-order) one bit in this BigInteger. * * Returns -1 if this BigInteger contains no one bits. */ public function getLowestSetBit() : int { $n = $this; $bitLength = $this->getBitLength(); for ($i = 0; $i <= $bitLength; $i++) { if ($n->isOdd()) { return $i; } $n = $n->shiftedRight(1); } return -1; } /** * Returns whether this number is even. */ public function isEven() : bool { return \in_array($this->value[-1], ['0', '2', '4', '6', '8'], true); } /** * Returns whether this number is odd. */ public function isOdd() : bool { return \in_array($this->value[-1], ['1', '3', '5', '7', '9'], true); } /** * Returns true if and only if the designated bit is set. * * Computes ((this & (1<<n)) != 0). * * @param int $n The bit to test, 0-based. * * @throws \InvalidArgumentException If the bit to test is negative. */ public function testBit(int $n) : bool { if ($n < 0) { throw new \InvalidArgumentException('The bit to test cannot be negative.'); } return $this->shiftedRight($n)->isOdd(); } public function compareTo(BigNumber|int|float|string $that) : int { $that = BigNumber::of($that); if ($that instanceof BigInteger) { return Calculator::get()->cmp($this->value, $that->value); } return - $that->compareTo($this); } public function getSign() : int { return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1); } public function toBigInteger() : BigInteger { return $this; } public function toBigDecimal() : BigDecimal { return self::newBigDecimal($this->value); } public function toBigRational() : BigRational { return self::newBigRational($this, BigInteger::one(), false); } public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal { return $this->toBigDecimal()->toScale($scale, $roundingMode); } public function toInt() : int { $intValue = (int) $this->value; if ($this->value !== (string) $intValue) { throw IntegerOverflowException::toIntOverflow($this); } return $intValue; } public function toFloat() : float { return (float) $this->value; } /** * Returns a string representation of this number in the given base. * * The output will always be lowercase for bases greater than 10. * * @throws \InvalidArgumentException If the base is out of range. */ public function toBase(int $base) : string { if ($base === 10) { return $this->value; } if ($base < 2 || $base > 36) { throw new \InvalidArgumentException(\sprintf('Base %d is out of range [2, 36]', $base)); } return Calculator::get()->toBase($this->value, $base); } /** * Returns a string representation of this number in an arbitrary base with a custom alphabet. * * Because this method accepts an alphabet with any character, including dash, it does not handle negative numbers; * a NegativeNumberException will be thrown when attempting to call this method on a negative number. * * @param string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8. * * @throws NegativeNumberException If this number is negative. * @throws \InvalidArgumentException If the given alphabet does not contain at least 2 chars. */ public function toArbitraryBase(string $alphabet) : string { $base = \strlen($alphabet); if ($base < 2) { throw new \InvalidArgumentException('The alphabet must contain at least 2 chars.'); } if ($this->value[0] === '-') { throw new NegativeNumberException(__FUNCTION__ . '() does not support negative numbers.'); } return Calculator::get()->toArbitraryBase($this->value, $alphabet, $base); } /** * Returns a string of bytes containing the binary representation of this BigInteger. * * The string is in big-endian byte-order: the most significant byte is in the zeroth element. * * If `$signed` is true, the output will be in two's-complement representation, and a sign bit will be prepended to * the output. If `$signed` is false, no sign bit will be prepended, and this method will throw an exception if the * number is negative. * * The string will contain the minimum number of bytes required to represent this BigInteger, including a sign bit * if `$signed` is true. * * This representation is compatible with the `fromBytes()` factory method, as long as the `$signed` flags match. * * @param bool $signed Whether to output a signed number in two's-complement representation with a leading sign bit. * * @throws NegativeNumberException If $signed is false, and the number is negative. */ public function toBytes(bool $signed = true) : string { if (! $signed && $this->isNegative()) { throw new NegativeNumberException('Cannot convert a negative number to a byte string when $signed is false.'); } $hex = $this->abs()->toBase(16); if (\strlen($hex) % 2 !== 0) { $hex = '0' . $hex; } $baseHexLength = \strlen($hex); if ($signed) { if ($this->isNegative()) { $bin = \hex2bin($hex); assert($bin !== false); $hex = \bin2hex(~$bin); $hex = self::fromBase($hex, 16)->plus(1)->toBase(16); $hexLength = \strlen($hex); if ($hexLength < $baseHexLength) { $hex = \str_repeat('0', $baseHexLength - $hexLength) . $hex; } if ($hex[0] < '8') { $hex = 'FF' . $hex; } } else { if ($hex[0] >= '8') { $hex = '00' . $hex; } } } return \hex2bin($hex); } public function __toString() : string { return $this->value; } /** * This method is required for serializing the object and SHOULD NOT be accessed directly. * * @internal * * @return array{value: string} */ public function __serialize(): array { return ['value' => $this->value]; } /** * This method is only here to allow unserializing the object and cannot be accessed directly. * * @internal * @psalm-suppress RedundantPropertyInitializationCheck * * @param array{value: string} $data * * @throws \LogicException */ public function __unserialize(array $data): void { if (isset($this->value)) { throw new \LogicException('__unserialize() is an internal function, it must not be called directly.'); } $this->value = $data['value']; } /** * This method is required by interface Serializable and SHOULD NOT be accessed directly. * * @internal */ public function serialize() : string { return $this->value; } /** * This method is only here to implement interface Serializable and cannot be accessed directly. * * @internal * @psalm-suppress RedundantPropertyInitializationCheck * * @throws \LogicException */ public function unserialize($value) : void { if (isset($this->value)) { throw new \LogicException('unserialize() is an internal function, it must not be called directly.'); } $this->value = $value; } } PK !>�Z����<