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\NumberFormatException;
10
use Brick\Math\Exception\RoundingNecessaryException;
11
 
12
/**
13
 * An arbitrarily large rational number.
14
 *
15
 * This class is immutable.
16
 *
17
 * @psalm-immutable
18
 */
19
final class BigRational extends BigNumber
20
{
21
    /**
22
     * The numerator.
23
     */
24
    private BigInteger $numerator;
25
 
26
    /**
27
     * The denominator. Always strictly positive.
28
     */
29
    private BigInteger $denominator;
30
 
31
    /**
32
     * Protected constructor. Use a factory method to obtain an instance.
33
     *
34
     * @param BigInteger $numerator        The numerator.
35
     * @param BigInteger $denominator      The denominator.
36
     * @param bool       $checkDenominator Whether to check the denominator for negative and zero.
37
     *
38
     * @throws DivisionByZeroException If the denominator is zero.
39
     */
40
    protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator)
41
    {
42
        if ($checkDenominator) {
43
            if ($denominator->isZero()) {
44
                throw DivisionByZeroException::denominatorMustNotBeZero();
45
            }
46
 
47
            if ($denominator->isNegative()) {
48
                $numerator   = $numerator->negated();
49
                $denominator = $denominator->negated();
50
            }
51
        }
52
 
53
        $this->numerator   = $numerator;
54
        $this->denominator = $denominator;
55
    }
56
 
57
    /**
58
     * Creates a BigRational of the given value.
59
     *
60
     * @param BigNumber|int|float|string $value
61
     *
62
     * @return BigRational
63
     *
64
     * @throws MathException If the value cannot be converted to a BigRational.
65
     *
66
     * @psalm-pure
67
     */
68
    public static function of($value) : BigNumber
69
    {
70
        return parent::of($value)->toBigRational();
71
    }
72
 
73
    /**
74
     * Creates a BigRational out of a numerator and a denominator.
75
     *
76
     * If the denominator is negative, the signs of both the numerator and the denominator
77
     * will be inverted to ensure that the denominator is always positive.
78
     *
79
     * @param BigNumber|int|float|string $numerator   The numerator. Must be convertible to a BigInteger.
80
     * @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger.
81
     *
82
     * @return BigRational
83
     *
84
     * @throws NumberFormatException      If an argument does not represent a valid number.
85
     * @throws RoundingNecessaryException If an argument represents a non-integer number.
86
     * @throws DivisionByZeroException    If the denominator is zero.
87
     *
88
     * @psalm-pure
89
     */
90
    public static function nd($numerator, $denominator) : BigRational
91
    {
92
        $numerator   = BigInteger::of($numerator);
93
        $denominator = BigInteger::of($denominator);
94
 
95
        return new BigRational($numerator, $denominator, true);
96
    }
97
 
98
    /**
99
     * Returns a BigRational representing zero.
100
     *
101
     * @return BigRational
102
     *
103
     * @psalm-pure
104
     */
105
    public static function zero() : BigRational
106
    {
107
        /**
108
         * @psalm-suppress ImpureStaticVariable
109
         * @var BigRational|null $zero
110
         */
111
        static $zero;
112
 
113
        if ($zero === null) {
114
            $zero = new BigRational(BigInteger::zero(), BigInteger::one(), false);
115
        }
116
 
117
        return $zero;
118
    }
119
 
120
    /**
121
     * Returns a BigRational representing one.
122
     *
123
     * @return BigRational
124
     *
125
     * @psalm-pure
126
     */
127
    public static function one() : BigRational
128
    {
129
        /**
130
         * @psalm-suppress ImpureStaticVariable
131
         * @var BigRational|null $one
132
         */
133
        static $one;
134
 
135
        if ($one === null) {
136
            $one = new BigRational(BigInteger::one(), BigInteger::one(), false);
137
        }
138
 
139
        return $one;
140
    }
141
 
142
    /**
143
     * Returns a BigRational representing ten.
144
     *
145
     * @return BigRational
146
     *
147
     * @psalm-pure
148
     */
149
    public static function ten() : BigRational
150
    {
151
        /**
152
         * @psalm-suppress ImpureStaticVariable
153
         * @var BigRational|null $ten
154
         */
155
        static $ten;
156
 
157
        if ($ten === null) {
158
            $ten = new BigRational(BigInteger::ten(), BigInteger::one(), false);
159
        }
160
 
161
        return $ten;
162
    }
163
 
164
    /**
165
     * @return BigInteger
166
     */
167
    public function getNumerator() : BigInteger
168
    {
169
        return $this->numerator;
170
    }
171
 
172
    /**
173
     * @return BigInteger
174
     */
175
    public function getDenominator() : BigInteger
176
    {
177
        return $this->denominator;
178
    }
179
 
180
    /**
181
     * Returns the quotient of the division of the numerator by the denominator.
182
     *
183
     * @return BigInteger
184
     */
185
    public function quotient() : BigInteger
186
    {
187
        return $this->numerator->quotient($this->denominator);
188
    }
189
 
190
    /**
191
     * Returns the remainder of the division of the numerator by the denominator.
192
     *
193
     * @return BigInteger
194
     */
195
    public function remainder() : BigInteger
196
    {
197
        return $this->numerator->remainder($this->denominator);
198
    }
199
 
200
    /**
201
     * Returns the quotient and remainder of the division of the numerator by the denominator.
202
     *
203
     * @return BigInteger[]
204
     */
205
    public function quotientAndRemainder() : array
206
    {
207
        return $this->numerator->quotientAndRemainder($this->denominator);
208
    }
209
 
210
    /**
211
     * Returns the sum of this number and the given one.
212
     *
213
     * @param BigNumber|int|float|string $that The number to add.
214
     *
215
     * @return BigRational The result.
216
     *
217
     * @throws MathException If the number is not valid.
218
     */
219
    public function plus($that) : BigRational
220
    {
221
        $that = BigRational::of($that);
222
 
223
        $numerator   = $this->numerator->multipliedBy($that->denominator);
224
        $numerator   = $numerator->plus($that->numerator->multipliedBy($this->denominator));
225
        $denominator = $this->denominator->multipliedBy($that->denominator);
226
 
227
        return new BigRational($numerator, $denominator, false);
228
    }
229
 
230
    /**
231
     * Returns the difference of this number and the given one.
232
     *
233
     * @param BigNumber|int|float|string $that The number to subtract.
234
     *
235
     * @return BigRational The result.
236
     *
237
     * @throws MathException If the number is not valid.
238
     */
239
    public function minus($that) : BigRational
240
    {
241
        $that = BigRational::of($that);
242
 
243
        $numerator   = $this->numerator->multipliedBy($that->denominator);
244
        $numerator   = $numerator->minus($that->numerator->multipliedBy($this->denominator));
245
        $denominator = $this->denominator->multipliedBy($that->denominator);
246
 
247
        return new BigRational($numerator, $denominator, false);
248
    }
249
 
250
    /**
251
     * Returns the product of this number and the given one.
252
     *
253
     * @param BigNumber|int|float|string $that The multiplier.
254
     *
255
     * @return BigRational The result.
256
     *
257
     * @throws MathException If the multiplier is not a valid number.
258
     */
259
    public function multipliedBy($that) : BigRational
260
    {
261
        $that = BigRational::of($that);
262
 
263
        $numerator   = $this->numerator->multipliedBy($that->numerator);
264
        $denominator = $this->denominator->multipliedBy($that->denominator);
265
 
266
        return new BigRational($numerator, $denominator, false);
267
    }
268
 
269
    /**
270
     * Returns the result of the division of this number by the given one.
271
     *
272
     * @param BigNumber|int|float|string $that The divisor.
273
     *
274
     * @return BigRational The result.
275
     *
276
     * @throws MathException If the divisor is not a valid number, or is zero.
277
     */
278
    public function dividedBy($that) : BigRational
279
    {
280
        $that = BigRational::of($that);
281
 
282
        $numerator   = $this->numerator->multipliedBy($that->denominator);
283
        $denominator = $this->denominator->multipliedBy($that->numerator);
284
 
285
        return new BigRational($numerator, $denominator, true);
286
    }
287
 
288
    /**
289
     * Returns this number exponentiated to the given value.
290
     *
291
     * @param int $exponent The exponent.
292
     *
293
     * @return BigRational The result.
294
     *
295
     * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
296
     */
297
    public function power(int $exponent) : BigRational
298
    {
299
        if ($exponent === 0) {
300
            $one = BigInteger::one();
301
 
302
            return new BigRational($one, $one, false);
303
        }
304
 
305
        if ($exponent === 1) {
306
            return $this;
307
        }
308
 
309
        return new BigRational(
310
            $this->numerator->power($exponent),
311
            $this->denominator->power($exponent),
312
            false
313
        );
314
    }
315
 
316
    /**
317
     * Returns the reciprocal of this BigRational.
318
     *
319
     * The reciprocal has the numerator and denominator swapped.
320
     *
321
     * @return BigRational
322
     *
323
     * @throws DivisionByZeroException If the numerator is zero.
324
     */
325
    public function reciprocal() : BigRational
326
    {
327
        return new BigRational($this->denominator, $this->numerator, true);
328
    }
329
 
330
    /**
331
     * Returns the absolute value of this BigRational.
332
     *
333
     * @return BigRational
334
     */
335
    public function abs() : BigRational
336
    {
337
        return new BigRational($this->numerator->abs(), $this->denominator, false);
338
    }
339
 
340
    /**
341
     * Returns the negated value of this BigRational.
342
     *
343
     * @return BigRational
344
     */
345
    public function negated() : BigRational
346
    {
347
        return new BigRational($this->numerator->negated(), $this->denominator, false);
348
    }
349
 
350
    /**
351
     * Returns the simplified value of this BigRational.
352
     *
353
     * @return BigRational
354
     */
355
    public function simplified() : BigRational
356
    {
357
        $gcd = $this->numerator->gcd($this->denominator);
358
 
359
        $numerator = $this->numerator->quotient($gcd);
360
        $denominator = $this->denominator->quotient($gcd);
361
 
362
        return new BigRational($numerator, $denominator, false);
363
    }
364
 
365
    /**
366
     * {@inheritdoc}
367
     */
368
    public function compareTo($that) : int
369
    {
370
        return $this->minus($that)->getSign();
371
    }
372
 
373
    /**
374
     * {@inheritdoc}
375
     */
376
    public function getSign() : int
377
    {
378
        return $this->numerator->getSign();
379
    }
380
 
381
    /**
382
     * {@inheritdoc}
383
     */
384
    public function toBigInteger() : BigInteger
385
    {
386
        $simplified = $this->simplified();
387
 
388
        if (! $simplified->denominator->isEqualTo(1)) {
389
            throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.');
390
        }
391
 
392
        return $simplified->numerator;
393
    }
394
 
395
    /**
396
     * {@inheritdoc}
397
     */
398
    public function toBigDecimal() : BigDecimal
399
    {
400
        return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator);
401
    }
402
 
403
    /**
404
     * {@inheritdoc}
405
     */
406
    public function toBigRational() : BigRational
407
    {
408
        return $this;
409
    }
410
 
411
    /**
412
     * {@inheritdoc}
413
     */
414
    public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
415
    {
416
        return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
417
    }
418
 
419
    /**
420
     * {@inheritdoc}
421
     */
422
    public function toInt() : int
423
    {
424
        return $this->toBigInteger()->toInt();
425
    }
426
 
427
    /**
428
     * {@inheritdoc}
429
     */
430
    public function toFloat() : float
431
    {
432
        $simplified = $this->simplified();
433
        return $simplified->numerator->toFloat() / $simplified->denominator->toFloat();
434
    }
435
 
436
    /**
437
     * {@inheritdoc}
438
     */
439
    public function __toString() : string
440
    {
441
        $numerator   = (string) $this->numerator;
442
        $denominator = (string) $this->denominator;
443
 
444
        if ($denominator === '1') {
445
            return $numerator;
446
        }
447
 
448
        return $this->numerator . '/' . $this->denominator;
449
    }
450
 
451
    /**
452
     * This method is required for serializing the object and SHOULD NOT be accessed directly.
453
     *
454
     * @internal
455
     *
456
     * @return array{numerator: BigInteger, denominator: BigInteger}
457
     */
458
    public function __serialize(): array
459
    {
460
        return ['numerator' => $this->numerator, 'denominator' => $this->denominator];
461
    }
462
 
463
    /**
464
     * This method is only here to allow unserializing the object and cannot be accessed directly.
465
     *
466
     * @internal
467
     * @psalm-suppress RedundantPropertyInitializationCheck
468
     *
469
     * @param array{numerator: BigInteger, denominator: BigInteger} $data
470
     *
471
     * @return void
472
     *
473
     * @throws \LogicException
474
     */
475
    public function __unserialize(array $data): void
476
    {
477
        if (isset($this->numerator)) {
478
            throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
479
        }
480
 
481
        $this->numerator = $data['numerator'];
482
        $this->denominator = $data['denominator'];
483
    }
484
 
485
    /**
486
     * This method is required by interface Serializable and SHOULD NOT be accessed directly.
487
     *
488
     * @internal
489
     *
490
     * @return string
491
     */
492
    public function serialize() : string
493
    {
494
        return $this->numerator . '/' . $this->denominator;
495
    }
496
 
497
    /**
498
     * This method is only here to implement interface Serializable and cannot be accessed directly.
499
     *
500
     * @internal
501
     * @psalm-suppress RedundantPropertyInitializationCheck
502
     *
503
     * @param string $value
504
     *
505
     * @return void
506
     *
507
     * @throws \LogicException
508
     */
509
    public function unserialize($value) : void
510
    {
511
        if (isset($this->numerator)) {
512
            throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
513
        }
514
 
515
        [$numerator, $denominator] = \explode('/', $value);
516
 
517
        $this->numerator   = BigInteger::of($numerator);
518
        $this->denominator = BigInteger::of($denominator);
519
    }
520
}