* @license http://opensource.org/licenses/MIT MIT */ declare (strict_types=1); namespace DeliciousBrains\WP_Offload_Media\Gcp\Ramsey\Uuid\Math; use DeliciousBrains\WP_Offload_Media\Gcp\Brick\Math\BigDecimal; use DeliciousBrains\WP_Offload_Media\Gcp\Brick\Math\BigInteger; use DeliciousBrains\WP_Offload_Media\Gcp\Brick\Math\Exception\MathException; use DeliciousBrains\WP_Offload_Media\Gcp\Brick\Math\RoundingMode as BrickMathRounding; use DeliciousBrains\WP_Offload_Media\Gcp\Ramsey\Uuid\Exception\InvalidArgumentException; use DeliciousBrains\WP_Offload_Media\Gcp\Ramsey\Uuid\Type\Decimal; use DeliciousBrains\WP_Offload_Media\Gcp\Ramsey\Uuid\Type\Hexadecimal; use DeliciousBrains\WP_Offload_Media\Gcp\Ramsey\Uuid\Type\Integer as IntegerObject; use DeliciousBrains\WP_Offload_Media\Gcp\Ramsey\Uuid\Type\NumberInterface; /** * A calculator using the brick/math library for arbitrary-precision arithmetic * * @psalm-immutable */ final class BrickMathCalculator implements CalculatorInterface { private const ROUNDING_MODE_MAP = [RoundingMode::UNNECESSARY => BrickMathRounding::UNNECESSARY, RoundingMode::UP => BrickMathRounding::UP, RoundingMode::DOWN => BrickMathRounding::DOWN, RoundingMode::CEILING => BrickMathRounding::CEILING, RoundingMode::FLOOR => BrickMathRounding::FLOOR, RoundingMode::HALF_UP => BrickMathRounding::HALF_UP, RoundingMode::HALF_DOWN => BrickMathRounding::HALF_DOWN, RoundingMode::HALF_CEILING => BrickMathRounding::HALF_CEILING, RoundingMode::HALF_FLOOR => BrickMathRounding::HALF_FLOOR, RoundingMode::HALF_EVEN => BrickMathRounding::HALF_EVEN]; public function add(NumberInterface $augend, NumberInterface ...$addends) : NumberInterface { $sum = BigInteger::of($augend->toString()); foreach ($addends as $addend) { $sum = $sum->plus($addend->toString()); } return new IntegerObject((string) $sum); } public function subtract(NumberInterface $minuend, NumberInterface ...$subtrahends) : NumberInterface { $difference = BigInteger::of($minuend->toString()); foreach ($subtrahends as $subtrahend) { $difference = $difference->minus($subtrahend->toString()); } return new IntegerObject((string) $difference); } public function multiply(NumberInterface $multiplicand, NumberInterface ...$multipliers) : NumberInterface { $product = BigInteger::of($multiplicand->toString()); foreach ($multipliers as $multiplier) { $product = $product->multipliedBy($multiplier->toString()); } return new IntegerObject((string) $product); } public function divide(int $roundingMode, int $scale, NumberInterface $dividend, NumberInterface ...$divisors) : NumberInterface { $brickRounding = $this->getBrickRoundingMode($roundingMode); $quotient = BigDecimal::of($dividend->toString()); foreach ($divisors as $divisor) { $quotient = $quotient->dividedBy($divisor->toString(), $scale, $brickRounding); } if ($scale === 0) { return new IntegerObject((string) $quotient->toBigInteger()); } return new Decimal((string) $quotient); } public function fromBase(string $value, int $base) : IntegerObject { try { return new IntegerObject((string) BigInteger::fromBase($value, $base)); } catch (MathException|\InvalidArgumentException $exception) { throw new InvalidArgumentException($exception->getMessage(), (int) $exception->getCode(), $exception); } } public function toBase(IntegerObject $value, int $base) : string { try { return BigInteger::of($value->toString())->toBase($base); } catch (MathException|\InvalidArgumentException $exception) { throw new InvalidArgumentException($exception->getMessage(), (int) $exception->getCode(), $exception); } } public function toHexadecimal(IntegerObject $value) : Hexadecimal { return new Hexadecimal($this->toBase($value, 16)); } public function toInteger(Hexadecimal $value) : IntegerObject { return $this->fromBase($value->toString(), 16); } /** * Maps ramsey/uuid rounding modes to those used by brick/math */ private function getBrickRoundingMode(int $roundingMode) : int { return self::ROUNDING_MODE_MAP[$roundingMode] ?? 0; } }