BcMath   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 336
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 29
lcom 2
cbo 2
dl 0
loc 336
ccs 66
cts 66
cp 1
rs 10
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A createInvalidLibraryException() 0 6 1
A __construct() 0 4 1
A add() 0 6 1
A subtract() 0 6 1
A multiply() 0 6 1
A divide() 0 6 1
A compare() 0 10 2
A modulus() 0 8 2
A power() 0 6 1
A squareRoot() 0 6 1
A absolute() 0 4 1
A negate() 0 4 1
A factorial() 0 4 1
A gcd() 0 4 1
A root() 0 4 1
A nextPrime() 0 4 1
A isPrime() 0 4 1
A isPerfectSquare() 0 4 1
A gamma() 0 4 1
A logGamma() 0 4 1
A supportsOperationType() 0 5 1
A isEnabled() 0 4 1
A getBcPrecision() 0 11 3
A isVersionComparison() 0 5 2
1
<?php
2
3
namespace Tdn\PhpTypes\Math\Library;
4
5
use Tdn\PhpTypes\Type\StringType;
6
7
/**
8
 * Class BcMath.
9
 */
10
class BcMath implements MathLibraryInterface
11
{
12
    /**
13
     * @var int
14
     */
15
    private $roundingStrategy;
16
17
    /**
18
     * @param int $roundingStrategy
19
     */
20 105
    public function __construct(int $roundingStrategy)
21
    {
22 105
        $this->roundingStrategy = $roundingStrategy;
23 105
    }
24
25
    /**
26
     * Add two arbitrary precision numbers.
27
     *
28
     * @param string $leftOperand
29
     * @param string $rightOperand
30
     * @param int    $precision
31
     *
32
     * @return string
33
     */
34 4
    public function add(string $leftOperand, string $rightOperand, int $precision = 0): string
35
    {
36 4
        $precision = $this->getBcPrecision($leftOperand, $precision);
37
38 4
        return \bcadd($leftOperand, $rightOperand, $precision);
39
    }
40
41
    /**
42
     * Subtract two arbitrary precision numbers.
43
     *
44
     * @param string $leftOperand
45
     * @param string $rightOperand
46
     * @param int    $precision
47
     *
48
     * @return string
49
     */
50 4
    public function subtract(string $leftOperand, string $rightOperand, int $precision = 0): string
51
    {
52 4
        $precision = $this->getBcPrecision($leftOperand, $precision);
53
54 4
        return \bcsub($leftOperand, $rightOperand, $precision);
55
    }
56
57
    /**
58
     * Multiply two arbitrary precision numbers.
59
     *
60
     * @param string $leftOperand
61
     * @param string $rightOperand
62
     * @param int    $precision
63
     *
64
     * @return string
65
     */
66 4
    public function multiply(string $leftOperand, string $rightOperand, int $precision = 0): string
67
    {
68 4
        $precision = $this->getBcPrecision($leftOperand, $precision);
69
70 4
        return bcmul($leftOperand, $rightOperand, $precision);
71
    }
72
73
    /**
74
     * Divide two arbitrary precision numbers.
75
     *
76
     * @param string $leftOperand
77
     * @param string $rightOperand
78
     * @param int    $precision
79
     *
80
     * @return string
81
     */
82 4
    public function divide(string $leftOperand, string $rightOperand, int $precision = 0): string
83
    {
84 4
        $precision = $this->getBcPrecision($leftOperand, $precision);
85
86 4
        return bcdiv($leftOperand, $rightOperand, $precision);
87
    }
88
89
    /**
90
     * Compare two arbitrary precision numbers.
91
     *
92
     * @param string $leftOperand
93
     * @param string $rightOperand
94
     * @param int    $precision
95
     *
96
     * @return string
97
     */
98 5
    public function compare(string $leftOperand, string $rightOperand, int $precision = 0): string
99
    {
100 5
        if ($this->isVersionComparison($leftOperand, $rightOperand)) {
101 2
            throw new \RuntimeException('BcMath cannot do version compare.');
102
        }
103
104 4
        $precision = $this->getBcPrecision($leftOperand, $precision);
105
106 4
        return strval(bccomp($leftOperand, $rightOperand, $precision));
107
    }
108
109
    /**
110
     * Get modulus of an arbitrary precision number.
111
     *
112
     * @param string $operand
113
     * @param string $modulus
114
     * @param int    $precision
115
     *
116
     * @return string
117
     */
118 5
    public function modulus(string $operand, string $modulus, int $precision = 0): string
119
    {
120 5
        if ($precision > 0) {
121 3
            throw new \RuntimeException('Precision is not supported. Use Spl::modulus, it uses fmod.');
122
        }
123
124 3
        return bcmod($operand, $modulus);
125
    }
126
127
    /**
128
     * Raise an arbitrary precision number to another.
129
     *
130
     * @param string $leftOperand
131
     * @param string $rightOperand
132
     * @param int    $precision
133
     *
134
     * @return string
135
     */
136 4
    public function power(string $leftOperand, string $rightOperand, int $precision = 0): string
137
    {
138 4
        $precision = $this->getBcPrecision($leftOperand, $precision);
139
140 4
        return bcpow($leftOperand, $rightOperand, $precision);
141
    }
142
143
    /**
144
     * Get the square root of an arbitrary precision number.
145
     *
146
     * @param string $operand
147
     * @param int    $precision
148
     *
149
     * @return string
150
     */
151 4
    public function squareRoot(string $operand, int $precision = 0): string
152
    {
153 4
        $bcPrecision = $this->getBcPrecision($operand, $precision) + 1;
154
155 4
        return (string) round(bcsqrt($operand, $bcPrecision), $precision, $this->roundingStrategy);
156
    }
157
158
    /**
159
     * Returns absolute value of operand.
160
     *
161
     * @param string $operand
162
     *
163
     * @return string
164
     */
165 4
    public function absolute(string $operand): string
166
    {
167 4
        throw $this->createInvalidLibraryException(__FUNCTION__);
168
    }
169
170
    /**
171
     * Negates a number. Opposite of absolute/abs.
172
     *
173
     * @param string $operand
174
     *
175
     * @return string
176
     */
177 4
    public function negate(string $operand): string
178
    {
179 4
        throw $this->createInvalidLibraryException(__FUNCTION__);
180
    }
181
182
    /**
183
     * Returns the factorial of operand.
184
     *
185
     * @param string $operand
186
     *
187
     * @return string
188
     */
189 4
    public function factorial(string $operand): string
190
    {
191 4
        throw $this->createInvalidLibraryException(__FUNCTION__);
192
    }
193
194
    /**
195
     * Greatest common divisor.
196
     *
197
     * @param string $leftOperand
198
     * @param string $rightOperand
199
     *
200
     * @return string
201
     */
202 4
    public function gcd(string $leftOperand, string $rightOperand): string
203
    {
204 4
        throw $this->createInvalidLibraryException(__FUNCTION__);
205
    }
206
207
    /**
208
     * Calculates to the nth root.
209
     *
210
     * @param string $operand
211
     * @param int    $nth
212
     *
213
     * @return string
214
     */
215 4
    public function root(string $operand, int $nth): string
216
    {
217 4
        throw $this->createInvalidLibraryException(__FUNCTION__);
218
    }
219
220
    /**
221
     * Gets the next prime after operand.
222
     *
223
     * @param string $operand
224
     *
225
     * @return string
226
     */
227 4
    public function nextPrime(string $operand): string
228
    {
229 4
        throw $this->createInvalidLibraryException(__FUNCTION__);
230
    }
231
232
    /**
233
     * @param string $operand
234
     * @param int    $reps
235
     *
236
     * @return bool
237
     */
238 4
    public function isPrime(string $operand, int $reps = 10): bool
239
    {
240 4
        throw $this->createInvalidLibraryException(__FUNCTION__);
241
    }
242
243
    /**
244
     * Checks if operand is perfect square.
245
     *
246
     * @param string $operand
247
     * @param int    $precision
248
     *
249
     * @return bool
250
     */
251 4
    public function isPerfectSquare(string $operand, int $precision = 0): bool
252
    {
253 4
        throw $this->createInvalidLibraryException(__FUNCTION__);
254
    }
255
256
    /**
257
     * The gamma function.
258
     *
259
     * @param string $operand
260
     *
261
     * @return string
262
     */
263 1
    public function gamma(string $operand): string
264
    {
265 1
        throw $this->createInvalidLibraryException(__FUNCTION__);
266
    }
267
268
    /**
269
     * The log-gamma function.
270
     *
271
     * @param string $operand
272
     *
273
     * @return string
274
     */
275 1
    public function logGamma(string $operand): string
276
    {
277 1
        throw $this->createInvalidLibraryException(__FUNCTION__);
278
    }
279
280
    /**
281
     * @param string $type
282
     *
283
     * @return bool
284
     */
285 48
    public function supportsOperationType(string $type): bool
286
    {
287
        //Supports both float and int.
288 48
        return true;
289
    }
290
291
    /**
292
     * @return bool
293
     */
294 48
    public function isEnabled(): bool
295
    {
296 48
        return extension_loaded('bcmath');
297
    }
298
299
    /**
300
     * Returns BCMath Precision.
301
     *
302
     * It differs from standard PHP precision in that it includes the numbers before the decimal period.
303
     * It always uses the left most operand to calculate precision.
304
     *
305
     * @param string $leftOperand
306
     * @param int    $precision
307
     *
308
     * @return int
309
     */
310 28
    private function getBcPrecision(string $leftOperand, int $precision = 0): int
311
    {
312 28
        if ($precision > 0) {
313 21
            $operand = StringType::create($leftOperand);
314 21
            if ($operand->contains('.')) {
315 21
                return $operand->countSubstr('.') + $precision;
316
            }
317
        }
318
319 24
        return $precision;
320
    }
321
322
    /**
323
     * @param string $leftOperand
324
     * @param string $rightOperand
325
     *
326
     * @return bool
327
     */
328 5
    private function isVersionComparison(string $leftOperand, string $rightOperand): bool
329
    {
330 5
        return (StringType::create($leftOperand)->isSemVer()->isTrue()) ||
331 5
        (StringType::create($rightOperand)->isSemVer()->isTrue());
332
    }
333
334
    /**
335
     * @param string $methodName
336
     *
337
     * @return \RuntimeException
338
     */
339 34
    private function createInvalidLibraryException(string $methodName): \RuntimeException
340
    {
341 34
        return new \RuntimeException(
342 34
            sprintf('Not a valid library for %s.', $methodName)
343
        );
344
    }
345
}
346