AbstractNumberType   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 310
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 93.75%

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 9
dl 0
loc 310
ccs 75
cts 80
cp 0.9375
rs 10
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
A plus() 0 4 1
A minus() 0 4 1
A multipliedBy() 0 4 1
A dividedBy() 0 4 1
A compare() 0 4 1
A modulus() 0 4 1
A power() 0 4 1
A squareRoot() 0 10 1
A absolute() 0 7 1
A negate() 0 7 1
A factorial() 0 7 1
A gcd() 0 4 1
A root() 0 7 1
A getNextPrime() 0 4 1
A isPrime() 0 4 1
A isPerfectSquare() 0 4 1
A getPrecision() 0 4 1
C asSubType() 0 22 8
A getMathAdapter() 0 4 1
A getAdapterOperation() 0 21 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Tdn\PhpTypes\Type;
6
7
use Tdn\PhpTypes\Exception\InvalidTransformationException;
8
use Tdn\PhpTypes\Math\DefaultMathAdapter;
9
use Tdn\PhpTypes\Math\MathAdapterInterface;
10
use Tdn\PhpTypes\Type\Traits\ValueType;
11
use Tdn\PhpTypes\Type\Traits\Boxable;
12
use Tdn\PhpTypes\Type\Traits\Transmutable;
13
14
/**
15
 * Class AbstractNumberType.
16
 */
17
abstract class AbstractNumberType implements NumberTypeInterface
18
{
19
    use ValueType;
20
    use Transmutable;
21
    use Boxable;
22
23
    /**
24
     * @var int
25
     */
26
    private $precision;
27
28
    /**
29
     * @var number
30
     */
31
    protected $value;
32
33
    /**
34
     * @var MathAdapterInterface
35
     */
36
    protected $mathAdapter;
37
38
    /**
39
     * AbstractNumberType constructor.
40
     * Precision order of priority: Argument != null > $num's precision > null precision.
41
     * So for an int, 0 should be passed for precision, otherwise it will auto-convert to float (if null or $num > 0).
42
     *
43
     * @param number                    $num
44
     * @param int|null                  $precision
45
     * @param MathAdapterInterface|null $mathAdapter
46
     */
47 60
    public function __construct($num, int $precision = null, MathAdapterInterface $mathAdapter = null)
48
    {
49 60
        $this->mathAdapter = $mathAdapter ?? new DefaultMathAdapter();
50 60
        $this->precision = $precision ?? $this->getMathAdapter()->getPrecision($num);
51 60
        $this->value = ($this->getPrecision() > 0) ?
52 60
            round($num, $this->getPrecision(), $this->mathAdapter->getRoundingStrategy()) : $num;
53 60
    }
54
55
    /**
56
     * Sums current NumberTypeInterface and number in argument.
57
     *
58
     * @param NumberTypeInterface|number|StringType|string $num
59
     *
60
     * @return NumberTypeInterface
61
     */
62 2
    public function plus($num): NumberTypeInterface
63
    {
64 2
        return $this->getAdapterOperation('add', $num);
65
    }
66
67
    /***
68
     * Subtracts number passed from current NumberTypeInterface.
69
     *
70
     * @param NumberTypeInterface|number|StringType|string $num
71
     *
72
     * @return NumberTypeInterface
73
     */
74 2
    public function minus($num): NumberTypeInterface
75
    {
76 2
        return $this->getAdapterOperation('subtract', $num);
77
    }
78
79
    /**
80
     * Multiplies current NumberTypeInterface by the number passed.
81
     *
82
     * @param NumberTypeInterface|number|StringType|string $num
83
     *
84
     * @return NumberTypeInterface
85
     */
86 2
    public function multipliedBy($num): NumberTypeInterface
87
    {
88 2
        return $this->getAdapterOperation('multiply', $num);
89
    }
90
91
    /**
92
     * Divides current NumberTypeInterface by the number passed.
93
     *
94
     * @param NumberTypeInterface|number|StringType|string $num
95
     *
96
     * @return NumberTypeInterface
97
     */
98 2
    public function dividedBy($num): NumberTypeInterface
99
    {
100 2
        return $this->getAdapterOperation('divide', $num);
101
    }
102
103
    /**
104
     * Compares current NumberTypeInterface to value passed.
105
     * Same rules as spaceship or version_compare.
106
     *
107
     * @param NumberTypeInterface|number|StringType|string $num
108
     *
109
     * @return NumberTypeInterface
110
     */
111 2
    public function compare($num): NumberTypeInterface
112
    {
113 2
        return $this->getAdapterOperation(__FUNCTION__, $num);
114
    }
115
116
    /**
117
     * Returns value of NumberTypeInterface modulus num.
118
     *
119
     * @param NumberTypeInterface|number|StringType|string $num
120
     *
121
     * @return NumberTypeInterface
122
     */
123 2
    public function modulus($num): NumberTypeInterface
124
    {
125 2
        return $this->getAdapterOperation(__FUNCTION__, $num);
126
    }
127
128
    /**
129
     * Returns NumberTypeInterface to the power of num.
130
     *
131
     * @param NumberTypeInterface|number|StringType|string $num
132
     *
133
     * @return NumberTypeInterface
134
     */
135 2
    public function power($num): NumberTypeInterface
136
    {
137 2
        return $this->getAdapterOperation(__FUNCTION__, $num);
138
    }
139
140
    /**
141
     * Returns the square root of NumberTypeInterface.
142
     *
143
     * @return NumberTypeInterface
144
     */
145 2
    public function squareRoot(): NumberTypeInterface
146
    {
147 2
        return static::valueOf(
148 2
            $this->getMathAdapter()->squareRoot(
149 2
                $this->toStringType()->get(),
150 2
                $this->getPrecision()
151
            ),
152 2
            $this->getPrecision()
153
        );
154
    }
155
156
    /**
157
     * Returns the absolute value of NumberTypeInterface.
158
     *
159
     * @return NumberTypeInterface
160
     */
161 2
    public function absolute(): NumberTypeInterface
162
    {
163 2
        return static::valueOf(
164 2
            $this->getMathAdapter()->absolute($this->toStringType()->get()),
165 2
            $this->getPrecision()
166
        );
167
    }
168
169
    /**
170
     * Returns the negated/opposite of NumberTypeInterface value.
171
     *
172
     * @return NumberTypeInterface
173
     */
174 2
    public function negate(): NumberTypeInterface
175
    {
176 2
        return static::valueOf(
177 2
            $this->getMathAdapter()->negate($this->toStringType()->get()),
178 2
            $this->getPrecision()
179
        );
180
    }
181
182
    /**
183
     * Returns NumberTypeInterface factorial.
184
     *
185
     * @return NumberTypeInterface
186
     */
187 2
    public function factorial(): NumberTypeInterface
188
    {
189 2
        return static::valueOf(
190 2
            $this->getMathAdapter()->factorial($this->toStringType()->get()),
191 2
            $this->getPrecision()
192
        );
193
    }
194
195
    /**
196
     * Returns the greatest common divider between NumberTypeInterface and num.
197
     *
198
     * @param NumberTypeInterface|number|StringType|string $num
199
     *
200
     * @return NumberTypeInterface
201
     */
202 2
    public function gcd($num): NumberTypeInterface
203
    {
204 2
        return $this->getAdapterOperation(__FUNCTION__, $num);
205
    }
206
207
    /**
208
     * Returns the root of NumberTypeInterface to the num.
209
     *
210
     * @param int $num
211
     *
212
     * @return NumberTypeInterface
213
     */
214 2
    public function root(int $num): NumberTypeInterface
215
    {
216 2
        return static::valueOf(
217 2
            $this->getMathAdapter()->root($this->toStringType()->get(), $num),
218 2
            $this->getPrecision()
219
        );
220
    }
221
222
    /**
223
     * Return the next prime number after NumberTypeInterface.
224
     *
225
     * @return NumberTypeInterface
226
     */
227 2
    public function getNextPrime(): NumberTypeInterface
228
    {
229 2
        return static::valueOf($this->getMathAdapter()->nextPrime($this->toStringType()->get()));
230
    }
231
232
    /**
233
     * Returns true of NumberTypeInterface is prime. False otherwise.
234
     *
235
     * @return BooleanType
236
     */
237 2
    public function isPrime(): BooleanType
238
    {
239 2
        return new BooleanType($this->getMathAdapter()->isPrime($this->toStringType()->get()));
240
    }
241
242
    /**
243
     * Returns true if NumberTypeInterface is a perfect square. False otherwise.
244
     *
245
     * @return BooleanType
246
     */
247 2
    public function isPerfectSquare(): BooleanType
248
    {
249 2
        return new BooleanType($this->getMathAdapter()->isPerfectSquare($this->toStringType()->get()));
250
    }
251
252
    /**
253
     * Gets the current precision (Should be 0 for IntType).
254
     *
255
     * @return int
256
     */
257 60
    public function getPrecision(): int
258
    {
259 60
        return $this->precision;
260
    }
261
262
    /**
263
     * @param callable $converterFunction
264
     * @param $mixed
265
     *
266
     * @return mixed
267
     */
268 48
    protected static function asSubType(callable $converterFunction, $mixed)
269
    {
270 48
        if ($mixed instanceof StringType || $mixed instanceof NumberTypeInterface) {
271 13
            $mixed = $mixed->get(); //Continue as primitive.
272
        }
273
274 48
        $type = strtolower(gettype($mixed));
275
        switch ($type) {
276 48
            case 'integer':
277 47
            case 'double':
278 46
            case 'float':
279 20
                return $converterFunction($mixed);
280 46
            case 'string':
281 34
                if (!is_numeric($mixed)) {
282 2
                    throw new InvalidTransformationException($type, static::class);
283
                }
284
285 32
                return $converterFunction($mixed);
286
            default:
287 12
                throw new InvalidTransformationException($type, static::class);
288
        }
289
    }
290
291
    /**
292
     * @return MathAdapterInterface
293
     */
294 46
    protected function getMathAdapter()
295
    {
296 46
        return $this->mathAdapter;
297
    }
298
299
    /**
300
     * @param string $operation
301
     * @param $operand
302
     *
303
     * @return NumberTypeInterface
304
     */
305 16
    private function getAdapterOperation(string $operation, $operand): NumberTypeInterface
306
    {
307 16
        if (!is_callable([$this->getMathAdapter(), $operation])) {
308
            throw new \LogicException(
309
                sprintf(
310
                    'Operation does not exist. Invalid operation: %s::%s()',
311
                    get_class($this->getMathAdapter()),
312
                    $operation
313
                )
314
            );
315
        }
316
317 16
        return static::valueOf(
318 16
            $this->getMathAdapter()->$operation(
319 16
                $this->toStringType()->get(),
320 16
                static::valueOf($operand)->toStringType()->get(),
321 16
                $this->getPrecision()
322
            ),
323 16
            $this->getPrecision()
324
        );
325
    }
326
}
327