Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
namespace Brick\Math;
6
 
7
use Brick\Math\Exception\DivisionByZeroException;
8
use Brick\Math\Exception\MathException;
9
use Brick\Math\Exception\NegativeNumberException;
10
use Brick\Math\Internal\Calculator;
11
 
12
/**
13
 * Immutable, arbitrary-precision signed decimal numbers.
14
 *
15
 * @psalm-immutable
16
 */
17
final class BigDecimal extends BigNumber
18
{
19
    /**
20
     * The unscaled value of this decimal number.
21
     *
22
     * This is a string of digits with an optional leading minus sign.
23
     * No leading zero must be present.
24
     * No leading minus sign must be present if the value is 0.
25
     */
26
    private string $value;
27
 
28
    /**
29
     * The scale (number of digits after the decimal point) of this decimal number.
30
     *
31
     * This must be zero or more.
32
     */
33
    private int $scale;
34
 
35
    /**
36
     * Protected constructor. Use a factory method to obtain an instance.
37
     *
38
     * @param string $value The unscaled value, validated.
39
     * @param int    $scale The scale, validated.
40
     */
41
    protected function __construct(string $value, int $scale = 0)
42
    {
43
        $this->value = $value;
44
        $this->scale = $scale;
45
    }
46
 
47
    /**
48
     * Creates a BigDecimal of the given value.
49
     *
50
     * @param BigNumber|int|float|string $value
51
     *
52
     * @return BigDecimal
53
     *
54
     * @throws MathException If the value cannot be converted to a BigDecimal.
55
     *
56
     * @psalm-pure
57
     */
58
    public static function of($value) : BigNumber
59
    {
60
        return parent::of($value)->toBigDecimal();
61
    }
62
 
63
    /**
64
     * Creates a BigDecimal from an unscaled value and a scale.
65
     *
66
     * Example: `(12345, 3)` will result in the BigDecimal `12.345`.
67
     *
68
     * @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
69
     * @param int                        $scale The scale of the number, positive or zero.
70
     *
71
     * @return BigDecimal
72
     *
73
     * @throws \InvalidArgumentException If the scale is negative.
74
     *
75
     * @psalm-pure
76
     */
77
    public static function ofUnscaledValue($value, int $scale = 0) : BigDecimal
78
    {
79
        if ($scale < 0) {
80
            throw new \InvalidArgumentException('The scale cannot be negative.');
81
        }
82
 
83
        return new BigDecimal((string) BigInteger::of($value), $scale);
84
    }
85
 
86
    /**
87
     * Returns a BigDecimal representing zero, with a scale of zero.
88
     *
89
     * @return BigDecimal
90
     *
91
     * @psalm-pure
92
     */
93
    public static function zero() : BigDecimal
94
    {
95
        /**
96
         * @psalm-suppress ImpureStaticVariable
97
         * @var BigDecimal|null $zero
98
         */
99
        static $zero;
100
 
101
        if ($zero === null) {
102
            $zero = new BigDecimal('0');
103
        }
104
 
105
        return $zero;
106
    }
107
 
108
    /**
109
     * Returns a BigDecimal representing one, with a scale of zero.
110
     *
111
     * @return BigDecimal
112
     *
113
     * @psalm-pure
114
     */
115
    public static function one() : BigDecimal
116
    {
117
        /**
118
         * @psalm-suppress ImpureStaticVariable
119
         * @var BigDecimal|null $one
120
         */
121
        static $one;
122
 
123
        if ($one === null) {
124
            $one = new BigDecimal('1');
125
        }
126
 
127
        return $one;
128
    }
129
 
130
    /**
131
     * Returns a BigDecimal representing ten, with a scale of zero.
132
     *
133
     * @return BigDecimal
134
     *
135
     * @psalm-pure
136
     */
137
    public static function ten() : BigDecimal
138
    {
139
        /**
140
         * @psalm-suppress ImpureStaticVariable
141
         * @var BigDecimal|null $ten
142
         */
143
        static $ten;
144
 
145
        if ($ten === null) {
146
            $ten = new BigDecimal('10');
147
        }
148
 
149
        return $ten;
150
    }
151
 
152
    /**
153
     * Returns the sum of this number and the given one.
154
     *
155
     * The result has a scale of `max($this->scale, $that->scale)`.
156
     *
157
     * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal.
158
     *
159
     * @return BigDecimal The result.
160
     *
161
     * @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
162
     */
163
    public function plus($that) : BigDecimal
164
    {
165
        $that = BigDecimal::of($that);
166
 
167
        if ($that->value === '0' && $that->scale <= $this->scale) {
168
            return $this;
169
        }
170
 
171
        if ($this->value === '0' && $this->scale <= $that->scale) {
172
            return $that;
173
        }
174
 
175
        [$a, $b] = $this->scaleValues($this, $that);
176
 
177
        $value = Calculator::get()->add($a, $b);
178
        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
179
 
180
        return new BigDecimal($value, $scale);
181
    }
182
 
183
    /**
184
     * Returns the difference of this number and the given one.
185
     *
186
     * The result has a scale of `max($this->scale, $that->scale)`.
187
     *
188
     * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal.
189
     *
190
     * @return BigDecimal The result.
191
     *
192
     * @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
193
     */
194
    public function minus($that) : BigDecimal
195
    {
196
        $that = BigDecimal::of($that);
197
 
198
        if ($that->value === '0' && $that->scale <= $this->scale) {
199
            return $this;
200
        }
201
 
202
        [$a, $b] = $this->scaleValues($this, $that);
203
 
204
        $value = Calculator::get()->sub($a, $b);
205
        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
206
 
207
        return new BigDecimal($value, $scale);
208
    }
209
 
210
    /**
211
     * Returns the product of this number and the given one.
212
     *
213
     * The result has a scale of `$this->scale + $that->scale`.
214
     *
215
     * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal.
216
     *
217
     * @return BigDecimal The result.
218
     *
219
     * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal.
220
     */
221
    public function multipliedBy($that) : BigDecimal
222
    {
223
        $that = BigDecimal::of($that);
224
 
225
        if ($that->value === '1' && $that->scale === 0) {
226
            return $this;
227
        }
228
 
229
        if ($this->value === '1' && $this->scale === 0) {
230
            return $that;
231
        }
232
 
233
        $value = Calculator::get()->mul($this->value, $that->value);
234
        $scale = $this->scale + $that->scale;
235
 
236
        return new BigDecimal($value, $scale);
237
    }
238
 
239
    /**
240
     * Returns the result of the division of this number by the given one, at the given scale.
241
     *
242
     * @param BigNumber|int|float|string $that         The divisor.
243
     * @param int|null                   $scale        The desired scale, or null to use the scale of this number.
244
     * @param int                        $roundingMode An optional rounding mode.
245
     *
246
     * @return BigDecimal
247
     *
248
     * @throws \InvalidArgumentException If the scale or rounding mode is invalid.
249
     * @throws MathException             If the number is invalid, is zero, or rounding was necessary.
250
     */
251
    public function dividedBy($that, ?int $scale = null, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
252
    {
253
        $that = BigDecimal::of($that);
254
 
255
        if ($that->isZero()) {
256
            throw DivisionByZeroException::divisionByZero();
257
        }
258
 
259
        if ($scale === null) {
260
            $scale = $this->scale;
261
        } elseif ($scale < 0) {
262
            throw new \InvalidArgumentException('Scale cannot be negative.');
263
        }
264
 
265
        if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) {
266
            return $this;
267
        }
268
 
269
        $p = $this->valueWithMinScale($that->scale + $scale);
270
        $q = $that->valueWithMinScale($this->scale - $scale);
271
 
272
        $result = Calculator::get()->divRound($p, $q, $roundingMode);
273
 
274
        return new BigDecimal($result, $scale);
275
    }
276
 
277
    /**
278
     * Returns the exact result of the division of this number by the given one.
279
     *
280
     * The scale of the result is automatically calculated to fit all the fraction digits.
281
     *
282
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
283
     *
284
     * @return BigDecimal The result.
285
     *
286
     * @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero,
287
     *                       or the result yields an infinite number of digits.
288
     */
289
    public function exactlyDividedBy($that) : BigDecimal
290
    {
291
        $that = BigDecimal::of($that);
292
 
293
        if ($that->value === '0') {
294
            throw DivisionByZeroException::divisionByZero();
295
        }
296
 
297
        [, $b] = $this->scaleValues($this, $that);
298
 
299
        $d = \rtrim($b, '0');
300
        $scale = \strlen($b) - \strlen($d);
301
 
302
        $calculator = Calculator::get();
303
 
304
        foreach ([5, 2] as $prime) {
305
            for (;;) {
306
                $lastDigit = (int) $d[-1];
307
 
308
                if ($lastDigit % $prime !== 0) {
309
                    break;
310
                }
311
 
312
                $d = $calculator->divQ($d, (string) $prime);
313
                $scale++;
314
            }
315
        }
316
 
317
        return $this->dividedBy($that, $scale)->stripTrailingZeros();
318
    }
319
 
320
    /**
321
     * Returns this number exponentiated to the given value.
322
     *
323
     * The result has a scale of `$this->scale * $exponent`.
324
     *
325
     * @param int $exponent The exponent.
326
     *
327
     * @return BigDecimal The result.
328
     *
329
     * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
330
     */
331
    public function power(int $exponent) : BigDecimal
332
    {
333
        if ($exponent === 0) {
334
            return BigDecimal::one();
335
        }
336
 
337
        if ($exponent === 1) {
338
            return $this;
339
        }
340
 
341
        if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
342
            throw new \InvalidArgumentException(\sprintf(
343
                'The exponent %d is not in the range 0 to %d.',
344
                $exponent,
345
                Calculator::MAX_POWER
346
            ));
347
        }
348
 
349
        return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent);
350
    }
351
 
352
    /**
353
     * Returns the quotient of the division of this number by this given one.
354
     *
355
     * The quotient has a scale of `0`.
356
     *
357
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
358
     *
359
     * @return BigDecimal The quotient.
360
     *
361
     * @throws MathException If the divisor is not a valid decimal number, or is zero.
362
     */
363
    public function quotient($that) : BigDecimal
364
    {
365
        $that = BigDecimal::of($that);
366
 
367
        if ($that->isZero()) {
368
            throw DivisionByZeroException::divisionByZero();
369
        }
370
 
371
        $p = $this->valueWithMinScale($that->scale);
372
        $q = $that->valueWithMinScale($this->scale);
373
 
374
        $quotient = Calculator::get()->divQ($p, $q);
375
 
376
        return new BigDecimal($quotient, 0);
377
    }
378
 
379
    /**
380
     * Returns the remainder of the division of this number by this given one.
381
     *
382
     * The remainder has a scale of `max($this->scale, $that->scale)`.
383
     *
384
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
385
     *
386
     * @return BigDecimal The remainder.
387
     *
388
     * @throws MathException If the divisor is not a valid decimal number, or is zero.
389
     */
390
    public function remainder($that) : BigDecimal
391
    {
392
        $that = BigDecimal::of($that);
393
 
394
        if ($that->isZero()) {
395
            throw DivisionByZeroException::divisionByZero();
396
        }
397
 
398
        $p = $this->valueWithMinScale($that->scale);
399
        $q = $that->valueWithMinScale($this->scale);
400
 
401
        $remainder = Calculator::get()->divR($p, $q);
402
 
403
        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
404
 
405
        return new BigDecimal($remainder, $scale);
406
    }
407
 
408
    /**
409
     * Returns the quotient and remainder of the division of this number by the given one.
410
     *
411
     * The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
412
     *
413
     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
414
     *
415
     * @return BigDecimal[] An array containing the quotient and the remainder.
416
     *
417
     * @throws MathException If the divisor is not a valid decimal number, or is zero.
418
     */
419
    public function quotientAndRemainder($that) : array
420
    {
421
        $that = BigDecimal::of($that);
422
 
423
        if ($that->isZero()) {
424
            throw DivisionByZeroException::divisionByZero();
425
        }
426
 
427
        $p = $this->valueWithMinScale($that->scale);
428
        $q = $that->valueWithMinScale($this->scale);
429
 
430
        [$quotient, $remainder] = Calculator::get()->divQR($p, $q);
431
 
432
        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
433
 
434
        $quotient = new BigDecimal($quotient, 0);
435
        $remainder = new BigDecimal($remainder, $scale);
436
 
437
        return [$quotient, $remainder];
438
    }
439
 
440
    /**
441
     * Returns the square root of this number, rounded down to the given number of decimals.
442
     *
443
     * @param int $scale
444
     *
445
     * @return BigDecimal
446
     *
447
     * @throws \InvalidArgumentException If the scale is negative.
448
     * @throws NegativeNumberException If this number is negative.
449
     */
450
    public function sqrt(int $scale) : BigDecimal
451
    {
452
        if ($scale < 0) {
453
            throw new \InvalidArgumentException('Scale cannot be negative.');
454
        }
455
 
456
        if ($this->value === '0') {
457
            return new BigDecimal('0', $scale);
458
        }
459
 
460
        if ($this->value[0] === '-') {
461
            throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
462
        }
463
 
464
        $value = $this->value;
465
        $addDigits = 2 * $scale - $this->scale;
466
 
467
        if ($addDigits > 0) {
468
            // add zeros
469
            $value .= \str_repeat('0', $addDigits);
470
        } elseif ($addDigits < 0) {
471
            // trim digits
472
            if (-$addDigits >= \strlen($this->value)) {
473
                // requesting a scale too low, will always yield a zero result
474
                return new BigDecimal('0', $scale);
475
            }
476
 
477
            $value = \substr($value, 0, $addDigits);
478
        }
479
 
480
        $value = Calculator::get()->sqrt($value);
481
 
482
        return new BigDecimal($value, $scale);
483
    }
484
 
485
    /**
486
     * Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
487
     *
488
     * @param int $n
489
     *
490
     * @return BigDecimal
491
     */
492
    public function withPointMovedLeft(int $n) : BigDecimal
493
    {
494
        if ($n === 0) {
495
            return $this;
496
        }
497
 
498
        if ($n < 0) {
499
            return $this->withPointMovedRight(-$n);
500
        }
501
 
502
        return new BigDecimal($this->value, $this->scale + $n);
503
    }
504
 
505
    /**
506
     * Returns a copy of this BigDecimal with the decimal point moved $n places to the right.
507
     *
508
     * @param int $n
509
     *
510
     * @return BigDecimal
511
     */
512
    public function withPointMovedRight(int $n) : BigDecimal
513
    {
514
        if ($n === 0) {
515
            return $this;
516
        }
517
 
518
        if ($n < 0) {
519
            return $this->withPointMovedLeft(-$n);
520
        }
521
 
522
        $value = $this->value;
523
        $scale = $this->scale - $n;
524
 
525
        if ($scale < 0) {
526
            if ($value !== '0') {
527
                $value .= \str_repeat('0', -$scale);
528
            }
529
            $scale = 0;
530
        }
531
 
532
        return new BigDecimal($value, $scale);
533
    }
534
 
535
    /**
536
     * Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
537
     *
538
     * @return BigDecimal
539
     */
540
    public function stripTrailingZeros() : BigDecimal
541
    {
542
        if ($this->scale === 0) {
543
            return $this;
544
        }
545
 
546
        $trimmedValue = \rtrim($this->value, '0');
547
 
548
        if ($trimmedValue === '') {
549
            return BigDecimal::zero();
550
        }
551
 
552
        $trimmableZeros = \strlen($this->value) - \strlen($trimmedValue);
553
 
554
        if ($trimmableZeros === 0) {
555
            return $this;
556
        }
557
 
558
        if ($trimmableZeros > $this->scale) {
559
            $trimmableZeros = $this->scale;
560
        }
561
 
562
        $value = \substr($this->value, 0, -$trimmableZeros);
563
        $scale = $this->scale - $trimmableZeros;
564
 
565
        return new BigDecimal($value, $scale);
566
    }
567
 
568
    /**
569
     * Returns the absolute value of this number.
570
     *
571
     * @return BigDecimal
572
     */
573
    public function abs() : BigDecimal
574
    {
575
        return $this->isNegative() ? $this->negated() : $this;
576
    }
577
 
578
    /**
579
     * Returns the negated value of this number.
580
     *
581
     * @return BigDecimal
582
     */
583
    public function negated() : BigDecimal
584
    {
585
        return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
586
    }
587
 
588
    /**
589
     * {@inheritdoc}
590
     */
591
    public function compareTo($that) : int
592
    {
593
        $that = BigNumber::of($that);
594
 
595
        if ($that instanceof BigInteger) {
596
            $that = $that->toBigDecimal();
597
        }
598
 
599
        if ($that instanceof BigDecimal) {
600
            [$a, $b] = $this->scaleValues($this, $that);
601
 
602
            return Calculator::get()->cmp($a, $b);
603
        }
604
 
605
        return - $that->compareTo($this);
606
    }
607
 
608
    /**
609
     * {@inheritdoc}
610
     */
611
    public function getSign() : int
612
    {
613
        return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
614
    }
615
 
616
    /**
617
     * @return BigInteger
618
     */
619
    public function getUnscaledValue() : BigInteger
620
    {
621
        return BigInteger::create($this->value);
622
    }
623
 
624
    /**
625
     * @return int
626
     */
627
    public function getScale() : int
628
    {
629
        return $this->scale;
630
    }
631
 
632
    /**
633
     * Returns a string representing the integral part of this decimal number.
634
     *
635
     * Example: `-123.456` => `-123`.
636
     *
637
     * @return string
638
     */
639
    public function getIntegralPart() : string
640
    {
641
        if ($this->scale === 0) {
642
            return $this->value;
643
        }
644
 
645
        $value = $this->getUnscaledValueWithLeadingZeros();
646
 
647
        return \substr($value, 0, -$this->scale);
648
    }
649
 
650
    /**
651
     * Returns a string representing the fractional part of this decimal number.
652
     *
653
     * If the scale is zero, an empty string is returned.
654
     *
655
     * Examples: `-123.456` => '456', `123` => ''.
656
     *
657
     * @return string
658
     */
659
    public function getFractionalPart() : string
660
    {
661
        if ($this->scale === 0) {
662
            return '';
663
        }
664
 
665
        $value = $this->getUnscaledValueWithLeadingZeros();
666
 
667
        return \substr($value, -$this->scale);
668
    }
669
 
670
    /**
671
     * Returns whether this decimal number has a non-zero fractional part.
672
     *
673
     * @return bool
674
     */
675
    public function hasNonZeroFractionalPart() : bool
676
    {
677
        return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
678
    }
679
 
680
    /**
681
     * {@inheritdoc}
682
     */
683
    public function toBigInteger() : BigInteger
684
    {
685
        $zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0);
686
 
687
        return BigInteger::create($zeroScaleDecimal->value);
688
    }
689
 
690
    /**
691
     * {@inheritdoc}
692
     */
693
    public function toBigDecimal() : BigDecimal
694
    {
695
        return $this;
696
    }
697
 
698
    /**
699
     * {@inheritdoc}
700
     */
701
    public function toBigRational() : BigRational
702
    {
703
        $numerator = BigInteger::create($this->value);
704
        $denominator = BigInteger::create('1' . \str_repeat('0', $this->scale));
705
 
706
        return BigRational::create($numerator, $denominator, false);
707
    }
708
 
709
    /**
710
     * {@inheritdoc}
711
     */
712
    public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
713
    {
714
        if ($scale === $this->scale) {
715
            return $this;
716
        }
717
 
718
        return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
719
    }
720
 
721
    /**
722
     * {@inheritdoc}
723
     */
724
    public function toInt() : int
725
    {
726
        return $this->toBigInteger()->toInt();
727
    }
728
 
729
    /**
730
     * {@inheritdoc}
731
     */
732
    public function toFloat() : float
733
    {
734
        return (float) (string) $this;
735
    }
736
 
737
    /**
738
     * {@inheritdoc}
739
     */
740
    public function __toString() : string
741
    {
742
        if ($this->scale === 0) {
743
            return $this->value;
744
        }
745
 
746
        $value = $this->getUnscaledValueWithLeadingZeros();
747
 
748
        return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
749
    }
750
 
751
    /**
752
     * This method is required for serializing the object and SHOULD NOT be accessed directly.
753
     *
754
     * @internal
755
     *
756
     * @return array{value: string, scale: int}
757
     */
758
    public function __serialize(): array
759
    {
760
        return ['value' => $this->value, 'scale' => $this->scale];
761
    }
762
 
763
    /**
764
     * This method is only here to allow unserializing the object and cannot be accessed directly.
765
     *
766
     * @internal
767
     * @psalm-suppress RedundantPropertyInitializationCheck
768
     *
769
     * @param array{value: string, scale: int} $data
770
     *
771
     * @return void
772
     *
773
     * @throws \LogicException
774
     */
775
    public function __unserialize(array $data): void
776
    {
777
        if (isset($this->value)) {
778
            throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
779
        }
780
 
781
        $this->value = $data['value'];
782
        $this->scale = $data['scale'];
783
    }
784
 
785
    /**
786
     * This method is required by interface Serializable and SHOULD NOT be accessed directly.
787
     *
788
     * @internal
789
     *
790
     * @return string
791
     */
792
    public function serialize() : string
793
    {
794
        return $this->value . ':' . $this->scale;
795
    }
796
 
797
    /**
798
     * This method is only here to implement interface Serializable and cannot be accessed directly.
799
     *
800
     * @internal
801
     * @psalm-suppress RedundantPropertyInitializationCheck
802
     *
803
     * @param string $value
804
     *
805
     * @return void
806
     *
807
     * @throws \LogicException
808
     */
809
    public function unserialize($value) : void
810
    {
811
        if (isset($this->value)) {
812
            throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
813
        }
814
 
815
        [$value, $scale] = \explode(':', $value);
816
 
817
        $this->value = $value;
818
        $this->scale = (int) $scale;
819
    }
820
 
821
    /**
822
     * Puts the internal values of the given decimal numbers on the same scale.
823
     *
824
     * @param BigDecimal $x The first decimal number.
825
     * @param BigDecimal $y The second decimal number.
826
     *
827
     * @return array{string, string} The scaled integer values of $x and $y.
828
     */
829
    private function scaleValues(BigDecimal $x, BigDecimal $y) : array
830
    {
831
        $a = $x->value;
832
        $b = $y->value;
833
 
834
        if ($b !== '0' && $x->scale > $y->scale) {
835
            $b .= \str_repeat('0', $x->scale - $y->scale);
836
        } elseif ($a !== '0' && $x->scale < $y->scale) {
837
            $a .= \str_repeat('0', $y->scale - $x->scale);
838
        }
839
 
840
        return [$a, $b];
841
    }
842
 
843
    /**
844
     * @param int $scale
845
     *
846
     * @return string
847
     */
848
    private function valueWithMinScale(int $scale) : string
849
    {
850
        $value = $this->value;
851
 
852
        if ($this->value !== '0' && $scale > $this->scale) {
853
            $value .= \str_repeat('0', $scale - $this->scale);
854
        }
855
 
856
        return $value;
857
    }
858
 
859
    /**
860
     * Adds leading zeros if necessary to the unscaled value to represent the full decimal number.
861
     *
862
     * @return string
863
     */
864
    private function getUnscaledValueWithLeadingZeros() : string
865
    {
866
        $value = $this->value;
867
        $targetLength = $this->scale + 1;
868
        $negative = ($value[0] === '-');
869
        $length = \strlen($value);
870
 
871
        if ($negative) {
872
            $length--;
873
        }
874
 
875
        if ($length >= $targetLength) {
876
            return $this->value;
877
        }
878
 
879
        if ($negative) {
880
            $value = \substr($value, 1);
881
        }
882
 
883
        $value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT);
884
 
885
        if ($negative) {
886
            $value = '-' . $value;
887
        }
888
 
889
        return $value;
890
    }
891
}