1<?php 2 3/** 4 * This file is part of the ramsey/uuid library 5 * 6 * For the full copyright and license information, please view the LICENSE 7 * file that was distributed with this source code. 8 * 9 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> 10 * @license http://opensource.org/licenses/MIT MIT 11 */ 12 13declare(strict_types=1); 14 15namespace Ramsey\Uuid\Math; 16 17use Brick\Math\BigDecimal; 18use Brick\Math\BigInteger; 19use Brick\Math\Exception\MathException; 20use Brick\Math\RoundingMode as BrickMathRounding; 21use Ramsey\Uuid\Exception\InvalidArgumentException; 22use Ramsey\Uuid\Type\Decimal; 23use Ramsey\Uuid\Type\Hexadecimal; 24use Ramsey\Uuid\Type\Integer as IntegerObject; 25use Ramsey\Uuid\Type\NumberInterface; 26 27/** 28 * A calculator using the brick/math library for arbitrary-precision arithmetic 29 * 30 * @psalm-immutable 31 */ 32final class BrickMathCalculator implements CalculatorInterface 33{ 34 private const ROUNDING_MODE_MAP = [ 35 RoundingMode::UNNECESSARY => BrickMathRounding::UNNECESSARY, 36 RoundingMode::UP => BrickMathRounding::UP, 37 RoundingMode::DOWN => BrickMathRounding::DOWN, 38 RoundingMode::CEILING => BrickMathRounding::CEILING, 39 RoundingMode::FLOOR => BrickMathRounding::FLOOR, 40 RoundingMode::HALF_UP => BrickMathRounding::HALF_UP, 41 RoundingMode::HALF_DOWN => BrickMathRounding::HALF_DOWN, 42 RoundingMode::HALF_CEILING => BrickMathRounding::HALF_CEILING, 43 RoundingMode::HALF_FLOOR => BrickMathRounding::HALF_FLOOR, 44 RoundingMode::HALF_EVEN => BrickMathRounding::HALF_EVEN, 45 ]; 46 47 public function add(NumberInterface $augend, NumberInterface ...$addends): NumberInterface 48 { 49 $sum = BigInteger::of($augend->toString()); 50 51 foreach ($addends as $addend) { 52 $sum = $sum->plus($addend->toString()); 53 } 54 55 return new IntegerObject((string) $sum); 56 } 57 58 public function subtract(NumberInterface $minuend, NumberInterface ...$subtrahends): NumberInterface 59 { 60 $difference = BigInteger::of($minuend->toString()); 61 62 foreach ($subtrahends as $subtrahend) { 63 $difference = $difference->minus($subtrahend->toString()); 64 } 65 66 return new IntegerObject((string) $difference); 67 } 68 69 public function multiply(NumberInterface $multiplicand, NumberInterface ...$multipliers): NumberInterface 70 { 71 $product = BigInteger::of($multiplicand->toString()); 72 73 foreach ($multipliers as $multiplier) { 74 $product = $product->multipliedBy($multiplier->toString()); 75 } 76 77 return new IntegerObject((string) $product); 78 } 79 80 public function divide( 81 int $roundingMode, 82 int $scale, 83 NumberInterface $dividend, 84 NumberInterface ...$divisors 85 ): NumberInterface { 86 $brickRounding = $this->getBrickRoundingMode($roundingMode); 87 88 $quotient = BigDecimal::of($dividend->toString()); 89 90 foreach ($divisors as $divisor) { 91 $quotient = $quotient->dividedBy($divisor->toString(), $scale, $brickRounding); 92 } 93 94 if ($scale === 0) { 95 return new IntegerObject((string) $quotient->toBigInteger()); 96 } 97 98 return new Decimal((string) $quotient); 99 } 100 101 public function fromBase(string $value, int $base): IntegerObject 102 { 103 try { 104 return new IntegerObject((string) BigInteger::fromBase($value, $base)); 105 } catch (MathException | \InvalidArgumentException $exception) { 106 throw new InvalidArgumentException( 107 $exception->getMessage(), 108 (int) $exception->getCode(), 109 $exception 110 ); 111 } 112 } 113 114 public function toBase(IntegerObject $value, int $base): string 115 { 116 try { 117 return BigInteger::of($value->toString())->toBase($base); 118 } catch (MathException | \InvalidArgumentException $exception) { 119 throw new InvalidArgumentException( 120 $exception->getMessage(), 121 (int) $exception->getCode(), 122 $exception 123 ); 124 } 125 } 126 127 public function toHexadecimal(IntegerObject $value): Hexadecimal 128 { 129 return new Hexadecimal($this->toBase($value, 16)); 130 } 131 132 public function toInteger(Hexadecimal $value): IntegerObject 133 { 134 return $this->fromBase($value->toString(), 16); 135 } 136 137 /** 138 * Maps ramsey/uuid rounding modes to those used by brick/math 139 */ 140 private function getBrickRoundingMode(int $roundingMode): int 141 { 142 return self::ROUNDING_MODE_MAP[$roundingMode] ?? 0; 143 } 144} 145