404

[ Avaa Bypassed ]




Upload:

Command:

elspacio@3.15.203.195: ~ $
<?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;

/**
 * Common interface for arbitrary-precision rational numbers.
 *
 * @psalm-immutable
 */
abstract class BigNumber implements \Serializable, \JsonSerializable
{
    /**
     * The regular expression used to parse integer, decimal and rational numbers.
     */
    private const PARSE_REGEXP =
        '/^' .
            '(?<sign>[\-\+])?' .
            '(?:' .
                '(?:' .
                    '(?<integral>[0-9]+)?' .
                    '(?<point>\.)?' .
                    '(?<fractional>[0-9]+)?' .
                    '(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
                ')|(?:' .
                    '(?<numerator>[0-9]+)' .
                    '\/?' .
                    '(?<denominator>[0-9]+)' .
                ')' .
            ')' .
        '$/';

    /**
     * Creates a BigNumber of the given value.
     *
     * The concrete return type is dependent on the given value, with the following rules:
     *
     * - BigNumber instances are returned as is
     * - integer numbers are returned as BigInteger
     * - floating point numbers are converted to a string then parsed as such
     * - strings containing a `/` character are returned as BigRational
     * - strings containing a `.` character or using an exponential notation are returned as BigDecimal
     * - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
     *
     * @throws NumberFormatException   If the format of the number is not valid.
     * @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
     *
     * @psalm-pure
     */
    public static function of(BigNumber|int|float|string $value) : BigNumber
    {
        if ($value instanceof BigNumber) {
            return $value;
        }

        if (\is_int($value)) {
            return new BigInteger((string) $value);
        }

        $value = \is_float($value) ? self::floatToString($value) : $value;

        $throw = static function() use ($value) : void {
            throw new NumberFormatException(\sprintf(
                'The given value "%s" does not represent a valid number.',
                $value
            ));
        };

        if (\preg_match(self::PARSE_REGEXP, $value, $matches) !== 1) {
            $throw();
        }

        $getMatch = static fn(string $value): ?string => (($matches[$value] ?? '') !== '') ? $matches[$value] : null;

        $sign        = $getMatch('sign');
        $numerator   = $getMatch('numerator');
        $denominator = $getMatch('denominator');

        if ($numerator !== null) {
            assert($denominator !== null);

            if ($sign !== null) {
                $numerator = $sign . $numerator;
            }

            $numerator   = self::cleanUp($numerator);
            $denominator = self::cleanUp($denominator);

            if ($denominator === '0') {
                throw DivisionByZeroException::denominatorMustNotBeZero();
            }

            return new BigRational(
                new BigInteger($numerator),
                new BigInteger($denominator),
                false
            );
        }

        $point      = $getMatch('point');
        $integral   = $getMatch('integral');
        $fractional = $getMatch('fractional');
        $exponent   = $getMatch('exponent');

        if ($integral === null && $fractional === null) {
            $throw();
        }

        if ($integral === null) {
            $integral = '0';
        }

        if ($point !== null || $exponent !== null) {
            $fractional = ($fractional ?? '');
            $exponent = ($exponent !== null) ? (int) $exponent : 0;

            if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) {
                throw new NumberFormatException('Exponent too large.');
            }

            $unscaledValue = self::cleanUp(($sign ?? ''). $integral . $fractional);

            $scale = \strlen($fractional) - $exponent;

            if ($scale < 0) {
                if ($unscaledValue !== '0') {
                    $unscaledValue .= \str_repeat('0', - $scale);
                }
                $scale = 0;
            }

            return new BigDecimal($unscaledValue, $scale);
        }

        $integral = self::cleanUp(($sign ?? '') . $integral);

        return new BigInteger($integral);
    }

    /**
     * Safely converts float to string, avoiding locale-dependent issues.
     *
     * @see https://github.com/brick/math/pull/20
     *
     * @psalm-pure
     * @psalm-suppress ImpureFunctionCall
     */
    private static function floatToString(float $float) : string
    {
        $currentLocale = \setlocale(LC_NUMERIC, '0');
        \setlocale(LC_NUMERIC, 'C');

        $result = (string) $float;

        \setlocale(LC_NUMERIC, $currentLocale);

        return $result;
    }

    /**
     * Proxy method to access BigInteger's protected constructor from sibling classes.
     *
     * @internal
     * @psalm-pure
     */
    protected function newBigInteger(string $value) : BigInteger
    {
        return new BigInteger($value);
    }

    /**
     * Proxy method to access BigDecimal's protected constructor from sibling classes.
     *
     * @internal
     * @psalm-pure
     */
    protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal
    {
        return new BigDecimal($value, $scale);
    }

    /**
     * Proxy method to access BigRational's protected constructor from sibling classes.
     *
     * @internal
     * @psalm-pure
     */
    protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational
    {
        return new BigRational($numerator, $denominator, $checkDenominator);
    }

    /**
     * Returns the minimum of the given values.
     *
     * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
     *                                              to an instance of the class this method is called on.
     *
     * @throws \InvalidArgumentException If no values are given.
     * @throws MathException             If an argument is not valid.
     *
     * @psalm-suppress LessSpecificReturnStatement
     * @psalm-suppress MoreSpecificReturnType
     * @psalm-pure
     */
    public static function min(BigNumber|int|float|string ...$values) : static
    {
        $min = null;

        foreach ($values as $value) {
            $value = static::of($value);

            if ($min === null || $value->isLessThan($min)) {
                $min = $value;
            }
        }

        if ($min === null) {
            throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
        }

        return $min;
    }

    /**
     * Returns the maximum of the given values.
     *
     * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
     *                                              to an instance of the class this method is called on.
     *
     * @throws \InvalidArgumentException If no values are given.
     * @throws MathException             If an argument is not valid.
     *
     * @psalm-suppress LessSpecificReturnStatement
     * @psalm-suppress MoreSpecificReturnType
     * @psalm-pure
     */
    public static function max(BigNumber|int|float|string ...$values) : static
    {
        $max = null;

        foreach ($values as $value) {
            $value = static::of($value);

            if ($max === null || $value->isGreaterThan($max)) {
                $max = $value;
            }
        }

        if ($max === null) {
            throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
        }

        return $max;
    }

    /**
     * Returns the sum of the given values.
     *
     * @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible
     *                                              to an instance of the class this method is called on.
     *
     * @throws \InvalidArgumentException If no values are given.
     * @throws MathException             If an argument is not valid.
     *
     * @psalm-pure
     */
    public static function sum(BigNumber|int|float|string ...$values) : static
    {
        /** @var static|null $sum */
        $sum = null;

        foreach ($values as $value) {
            $value = static::of($value);

            $sum = $sum === null ? $value : self::add($sum, $value);
        }

        if ($sum === null) {
            throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
        }

        return $sum;
    }

    /**
     * Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
     *
     * @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to
     *       concrete classes the responsibility to perform the addition themselves or delegate it to the given number,
     *       depending on their ability to perform the operation. This will also require a version bump because we're
     *       potentially breaking custom BigNumber implementations (if any...)
     *
     * @psalm-pure
     */
    private static function add(BigNumber $a, BigNumber $b) : BigNumber
    {
        if ($a instanceof BigRational) {
            return $a->plus($b);
        }

        if ($b instanceof BigRational) {
            return $b->plus($a);
        }

        if ($a instanceof BigDecimal) {
            return $a->plus($b);
        }

        if ($b instanceof BigDecimal) {
            return $b->plus($a);
        }

        /** @var BigInteger $a */

        return $a->plus($b);
    }

    /**
     * Removes optional leading zeros and + sign from the given number.
     *
     * @param string $number The number, validated as a non-empty string of digits with optional leading sign.
     *
     * @psalm-pure
     */
    private static function cleanUp(string $number) : string
    {
        $firstChar = $number[0];

        if ($firstChar === '+' || $firstChar === '-') {
            $number = \substr($number, 1);
        }

        $number = \ltrim($number, '0');

        if ($number === '') {
            return '0';
        }

        if ($firstChar === '-') {
            return '-' . $number;
        }

        return $number;
    }

    /**
     * Checks if this number is equal to the given one.
     */
    public function isEqualTo(BigNumber|int|float|string $that) : bool
    {
        return $this->compareTo($that) === 0;
    }

    /**
     * Checks if this number is strictly lower than the given one.
     */
    public function isLessThan(BigNumber|int|float|string $that) : bool
    {
        return $this->compareTo($that) < 0;
    }

    /**
     * Checks if this number is lower than or equal to the given one.
     */
    public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool
    {
        return $this->compareTo($that) <= 0;
    }

    /**
     * Checks if this number is strictly greater than the given one.
     */
    public function isGreaterThan(BigNumber|int|float|string $that) : bool
    {
        return $this->compareTo($that) > 0;
    }

    /**
     * Checks if this number is greater than or equal to the given one.
     */
    public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool
    {
        return $this->compareTo($that) >= 0;
    }

    /**
     * Checks if this number equals zero.
     */
    public function isZero() : bool
    {
        return $this->getSign() === 0;
    }

    /**
     * Checks if this number is strictly negative.
     */
    public function isNegative() : bool
    {
        return $this->getSign() < 0;
    }

    /**
     * Checks if this number is negative or zero.
     */
    public function isNegativeOrZero() : bool
    {
        return $this->getSign() <= 0;
    }

    /**
     * Checks if this number is strictly positive.
     */
    public function isPositive() : bool
    {
        return $this->getSign() > 0;
    }

    /**
     * Checks if this number is positive or zero.
     */
    public function isPositiveOrZero() : bool
    {
        return $this->getSign() >= 0;
    }

    /**
     * Returns the sign of this number.
     *
     * @return int -1 if the number is negative, 0 if zero, 1 if positive.
     */
    abstract public function getSign() : int;

    /**
     * Compares this number to the given one.
     *
     * @return int [-1,0,1] If `$this` is lower than, equal to, or greater than `$that`.
     *
     * @throws MathException If the number is not valid.
     */
    abstract public function compareTo(BigNumber|int|float|string $that) : int;

    /**
     * Converts this number to a BigInteger.
     *
     * @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
     */
    abstract public function toBigInteger() : BigInteger;

    /**
     * Converts this number to a BigDecimal.
     *
     * @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
     */
    abstract public function toBigDecimal() : BigDecimal;

    /**
     * Converts this number to a BigRational.
     */
    abstract public function toBigRational() : BigRational;

    /**
     * Converts this number to a BigDecimal with the given scale, using rounding if necessary.
     *
     * @param int $scale        The scale of the resulting `BigDecimal`.
     * @param int $roundingMode A `RoundingMode` constant.
     *
     * @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding.
     *                                    This only applies when RoundingMode::UNNECESSARY is used.
     */
    abstract public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;

    /**
     * Returns the exact value of this number as a native integer.
     *
     * If this number cannot be converted to a native integer without losing precision, an exception is thrown.
     * Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
     *
     * @throws MathException If this number cannot be exactly converted to a native integer.
     */
    abstract public function toInt() : int;

    /**
     * Returns an approximation of this number as a floating-point value.
     *
     * Note that this method can discard information as the precision of a floating-point value
     * is inherently limited.
     *
     * If the number is greater than the largest representable floating point number, positive infinity is returned.
     * If the number is less than the smallest representable floating point number, negative infinity is returned.
     */
    abstract public function toFloat() : float;

    /**
     * Returns a string representation of this number.
     *
     * The output of this method can be parsed by the `of()` factory method;
     * this will yield an object equal to this one, without any information loss.
     */
    abstract public function __toString() : string;

    public function jsonSerialize() : string
    {
        return $this->__toString();
    }
}

Filemanager

Name Type Size Permission Actions
Exception Folder 0755
Internal Folder 0755
BigDecimal.php File 21.82 KB 0644
BigInteger.php File 32.75 KB 0644
BigNumber.php File 15.24 KB 0644
BigRational.php File 12.51 KB 0644
RoundingMode.php File 3.77 KB 0644