Test Failed
Pull Request — master (#140)
by Jordan
18:33
created

Numbers::makeOrDont()   B

Complexity

Conditions 11
Paths 8

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
dl 0
loc 31
rs 7.3166
c 0
b 0
f 0
eloc 18
nc 8
nop 4

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Samsara\Fermat\Core;
4
5
use Samsara\Exceptions\UsageError\IntegrityConstraint;
6
use Samsara\Fermat\Core\Enums\NumberBase;
7
use Samsara\Fermat\Core\Provider\ConstantProvider;
8
use Samsara\Fermat\Core\Types\Base\Interfaces\Numbers\DecimalInterface;
9
use Samsara\Fermat\Core\Types\Base\Interfaces\Numbers\FractionInterface;
10
use Samsara\Fermat\Core\Types\Base\Interfaces\Numbers\NumberInterface;
11
use Samsara\Fermat\Core\Values\ImmutableFraction;
12
use Samsara\Fermat\Core\Values\ImmutableDecimal;
13
use Samsara\Fermat\Core\Values\MutableFraction;
14
use Samsara\Fermat\Core\Values\MutableDecimal;
15
16
/**
17
 * This class contains useful factory methods to create various numbers, verify the
18
 * class of a given number, and generally handle all of the formatting necessary to
19
 * satisfy the various constructors of valid value objects.
20
 *
21
 * @package Samsara\Fermat\Core
22
 */
23
class Numbers
24
{
25
26
    public const MUTABLE = MutableDecimal::class;
27
    public const IMMUTABLE = ImmutableDecimal::class;
28
    public const MUTABLE_FRACTION = MutableFraction::class;
29
    public const IMMUTABLE_FRACTION = ImmutableFraction::class;
30
    /* 105 digits after decimal, which is going to be overkill in almost all places */
31
    public const PI = '3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679';
32
    /* Tau (2pi) to 100 digits */
33
    public const TAU = '6.2831853071795864769252867665590057683943387987502116419498891846156328125724179972560696506842341359';
34
    /* Euler's Number to 100 digits */
35
    public const E = '2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427';
36
    /* Golden Ratio to 100 digits */
37
    public const GOLDEN_RATIO = '1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137';
38
    /* Natural log of 10 to 100 digits */
39
    public const LN_10 = '2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298';
40
    /* Natural log of 2 to 100 digits */
41
    public const LN_2 = '0.693147180559945309417232121458176568075500134360255254120680009493393621969694715605863326996418687';
42
    /* The value of i^i */
43
    public const I_POW_I = '0.2078795763507619085469556198349787700338778416317696080751358830554198772854821397886002778654260353';
44
45
    /**
46
     * This class will make and return an instance of a concrete value.
47
     *
48
     * The main reason for this class is that you can pass an unknown value instance as the
49
     * $type parameter and it will behave as if you passed the FQCN.
50
     *
51
     * @param mixed         $type   An instance of FQCN for any Fermat value class.
52
     * @param mixed         $value  Any value which is valid for the constructor which will be called.
53
     * @param int|null      $scale  The scale setting the created instance should have.
54
     * @param NumberBase    $base   The base to create the number in. Note, this is not the same as the base of $value, which is always base-10
55
     *
56
     * @return ImmutableDecimal|MutableDecimal|ImmutableFraction|MutableFraction|NumberInterface|FractionInterface
57
     * @throws IntegrityConstraint
58
     */
59
    public static function make(mixed $type, mixed $value, ?int $scale = null, NumberBase $base = NumberBase::Ten)
60
    {
61
62
        if (is_object($type)) {
63
            $type = get_class($type);
64
        }
65
66
        if ($type === static::IMMUTABLE) {
67
            return new ImmutableDecimal(trim($value), $scale, $base, true);
68
        }
69
70
        if ($type === static::MUTABLE) {
71
            return new MutableDecimal(trim($value), $scale, $base, true);
72
        }
73
74
        if ($type === static::IMMUTABLE_FRACTION) {
75
            return self::makeFractionFromString($type, $value, $base);
76
        }
77
78
        if ($type === static::MUTABLE_FRACTION) {
79
            return self::makeFractionFromString($type, $value, $base);
80
        }
81
82
        throw new IntegrityConstraint(
83
            '$type must be an implemented concrete class that is supported',
84
            'Provide a type that implements NumberInterface or CoordinateInterface (the Numbers class contains constants for the built in ones)',
85
            'The $type argument was not an implementation of NumberInterface or CoordinateInterface'
86
        );
87
    }
88
89
    /**
90
     * @param $type
91
     * @param $value
92
     * @param int|null $scale
93
     * @param NumberBase $base
94
     *
95
     * @return NumberInterface
96
     * @throws IntegrityConstraint
97
     */
98
    public static function makeFromBase10($type, $value, ?int $scale = null, NumberBase $base = NumberBase::Ten): NumberInterface
99
    {
100
        /**
101
         * @var ImmutableDecimal|MutableDecimal $number
102
         */
103
        $number = self::make($type, $value, $scale);
104
105
        return $number->setBase($base);
106
    }
107
108
    /**
109
     * @param string|object $type
110
     * @param int|float|string|array|NumberInterface|DecimalInterface|FractionInterface $value
111
     * @param int|null $scale
112
     * @param NumberBase $base
113
     *
114
     * @return ImmutableDecimal|MutableDecimal|NumberInterface|ImmutableDecimal[]|MutableDecimal[]|NumberInterface[]
115
     * @throws IntegrityConstraint
116
     */
117
    public static function makeOrDont(string|object $type, mixed $value, ?int $scale = null, NumberBase $base = NumberBase::Ten)
118
    {
119
120
        if (is_object($value)) {
121
            if ($value instanceof $type) {
122
                return $value;
123
            }
124
125
            if ($value instanceof NumberInterface) {
126
                return static::make($type, $value->getValue(NumberBase::Ten), $scale, $base);
0 ignored issues
show
Unused Code introduced by
The call to Samsara\Fermat\Core\Type...erInterface::getValue() has too many arguments starting with Samsara\Fermat\Core\Enums\NumberBase::Ten. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

126
                return static::make($type, $value->/** @scrutinizer ignore-call */ getValue(NumberBase::Ten), $scale, $base);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
127
            }
128
        } elseif (is_array($value)) {
129
            $newInput = [];
130
131
            foreach ($value as $key => $item) {
132
                $newInput[$key] = static::makeOrDont($type, $item, $scale, $base);
133
            }
134
135
            return $newInput;
136
        } elseif (is_string($value) || is_int($value) || is_float($value)) {
137
            $isImaginary = str_contains($value, 'i');
138
139
            if (is_numeric($value) || $isImaginary) {
140
                return static::make($type, $value, $scale, $base);
141
            }
142
        }
143
144
        throw new IntegrityConstraint(
145
            '$input must be an int, float, numeric string, or an implementation of NumberInterface',
146
            'Provide any of the MANY valid inputs',
147
            'The $input argument was not numeric or an implementation of NumberInterface. Given value: '.$value
148
        );
149
150
    }
151
152
    /**
153
     * @param string $type
154
     * @param string $value
155
     * @param NumberBase $base
156
     *
157
     * @return FractionInterface
158
     * @throws IntegrityConstraint
159
     */
160
    public static function makeFractionFromString(string $type, string $value, NumberBase $base = NumberBase::Ten): FractionInterface
161
    {
162
        $parts = explode('/', $value);
163
164
        if (count($parts) > 2) {
165
            throw new IntegrityConstraint(
166
                'Only one division symbol (/) can be used',
167
                'Change the calling code to not provide more than one division symbol',
168
                'makeFractionFromString needs either one or zero division symbols in the $value argument; '.$value.' given'
169
            );
170
        }
171
172
        /** @var ImmutableDecimal $numerator */
173
        $numerator = self::make(self::IMMUTABLE, trim($parts[0]));
174
        /** @var ImmutableDecimal $denominator */
175
        $denominator = isset($parts[1]) ? self::make(self::IMMUTABLE, trim($parts[1])) : self::makeOne();
176
177
        if ($type === self::IMMUTABLE_FRACTION) {
178
            return new ImmutableFraction($numerator, $denominator, $base);
179
        }
180
181
        if ($type === self::MUTABLE_FRACTION) {
182
            return new MutableFraction($numerator, $denominator, $base);
183
        }
184
185
        throw new IntegrityConstraint(
186
            'Type must be an implementation of FractionInterface',
187
            'Alter to calling code to use the correct type',
188
            'makeFractionFromString can only make objects which implement the FractionInterface; '.$type.' given'
189
        );
190
    }
191
192
    /**
193
     * @param int|null $scale
194
     *
195
     * @return ImmutableDecimal
196
     * @throws IntegrityConstraint
197
     */
198
    public static function makePi(int $scale = null): ImmutableDecimal
199
    {
200
201
        if (!is_null($scale)) {
202
            if ($scale < 1) {
203
                throw new IntegrityConstraint(
204
                    '$scale must be at least 1',
205
                    'Provide a scale within range',
206
                    'The pi constant cannot have a scale less than 1'
207
                );
208
            }
209
210
            if ($scale > 100) {
211
                return self::make(self::IMMUTABLE, ConstantProvider::makePi($scale), $scale);
212
            }
213
214
            return self::make(self::IMMUTABLE, self::PI, $scale+1)->truncateToScale($scale);
215
        }
216
217
        return self::make(self::IMMUTABLE, self::PI, 100);
218
219
    }
220
221
    /**
222
     * @param null $scale
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $scale is correct as it would always require null to be passed?
Loading history...
223
     *
224
     * @return ImmutableDecimal
225
     * @throws IntegrityConstraint
226
     */
227
    public static function makeTau($scale = null): ImmutableDecimal
228
    {
229
        if (!is_null($scale)) {
0 ignored issues
show
introduced by
The condition is_null($scale) is always true.
Loading history...
230
            if ($scale < 1) {
231
                throw new IntegrityConstraint(
232
                    '$scale must be at least 1',
233
                    'Provide a scale within range',
234
                    'The E constant cannot have a scale less than 1'
235
                );
236
            }
237
238
            if ($scale > 100) {
239
                $pi = self::make(self::IMMUTABLE, ConstantProvider::makePi($scale+2), $scale + 2);
240
                /** @var ImmutableDecimal */
241
                return $pi->multiply(2)->truncateToScale($scale);
242
            }
243
244
            return self::make(self::IMMUTABLE, self::TAU, $scale+1)->truncateToScale($scale);
245
        }
246
247
        return self::make(self::IMMUTABLE, self::TAU, 100);
248
    }
249
250
    /**
251
     * @param int|null $scale
252
     *
253
     * @return ImmutableDecimal
254
     * @throws IntegrityConstraint
255
     */
256
    public static function make2Pi(int $scale = null): ImmutableDecimal
257
    {
258
        return self::makeTau($scale);
0 ignored issues
show
Bug introduced by
It seems like $scale can also be of type integer; however, parameter $scale of Samsara\Fermat\Core\Numbers::makeTau() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

258
        return self::makeTau(/** @scrutinizer ignore-type */ $scale);
Loading history...
259
    }
260
261
    /**
262
     * @param int|null $scale
263
     *
264
     * @return ImmutableDecimal
265
     * @throws IntegrityConstraint
266
     */
267
    public static function makeE(int $scale = null): ImmutableDecimal
268
    {
269
270
        if (!is_null($scale)) {
271
            if ($scale < 1) {
272
                throw new IntegrityConstraint(
273
                    '$scale must be at least 1',
274
                    'Provide a scale within range',
275
                    'The E constant cannot have a scale less than 1'
276
                );
277
            }
278
279
            if ($scale > 100) {
280
                return self::make(self::IMMUTABLE, ConstantProvider::makeE($scale), $scale);
281
            }
282
283
            return self::make(self::IMMUTABLE, self::E, $scale+1)->truncateToScale($scale);
284
        }
285
286
        return self::make(self::IMMUTABLE, self::E, 100);
287
288
    }
289
290
    /**
291
     * @param int|null $scale
292
     *
293
     * @return ImmutableDecimal
294
     * @throws IntegrityConstraint
295
     */
296
    public static function makeGoldenRatio(?int $scale = null): ImmutableDecimal
297
    {
298
299
        if (!is_null($scale)) {
300
            if ($scale < 1) {
301
                throw new IntegrityConstraint(
302
                    '$scale must be at least 1',
303
                    'Provide a scale within range',
304
                    'The Phi constant (Golden Ratio) cannot have a scale less than 1'
305
                );
306
            }
307
308
            if ($scale > 100) {
309
                return self::make(self::IMMUTABLE, ConstantProvider::makeGoldenRatio($scale), $scale);
310
            }
311
312
            return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, $scale+1)->truncateToScale($scale);
0 ignored issues
show
Bug introduced by
The method truncateToScale() does not exist on Samsara\Fermat\Core\Type...mbers\FractionInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

312
            return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, $scale+1)->/** @scrutinizer ignore-call */ truncateToScale($scale);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
313
        }
314
315
        return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, 100);
316
317
    }
318
319
    /**
320
     * @param int|null $scale
321
     *
322
     * @return ImmutableDecimal
323
     * @throws IntegrityConstraint
324
     */
325
    public static function makeNaturalLog10(?int $scale = null): ImmutableDecimal
326
    {
327
328
        if (!is_null($scale)) {
329
            if ($scale < 1) {
330
                throw new IntegrityConstraint(
331
                    '$scale must be at least 1',
332
                    'Provide a scale within range',
333
                    'The natural log of 10 constant cannot have a scale less than 1'
334
                );
335
            }
336
337
            if ($scale > 100) {
338
                return self::make(self::IMMUTABLE, ConstantProvider::makeLn10($scale), $scale);
339
            }
340
341
            return self::make(self::IMMUTABLE, self::LN_10, $scale+1)->truncateToScale($scale);
342
        }
343
344
        return self::make(self::IMMUTABLE, self::LN_10, 100);
345
346
    }
347
348
    /**
349
     * @param int|null $scale
350
     *
351
     * @return ImmutableDecimal
352
     * @throws IntegrityConstraint
353
     */
354
    public static function makeNaturalLog2(?int $scale = null): ImmutableDecimal
355
    {
356
357
        if (!is_null($scale)) {
358
            if ($scale < 1) {
359
                throw new IntegrityConstraint(
360
                    '$scale must be at least 1',
361
                    'Provide a scale within range',
362
                    'The natural log of 10 constant cannot have a scale less than 1'
363
                );
364
            }
365
366
            if ($scale > 100) {
367
                return self::make(self::IMMUTABLE, ConstantProvider::makeLn2($scale), $scale);
368
            }
369
370
            return self::make(self::IMMUTABLE, self::LN_2, $scale+1)->truncateToScale($scale);
371
        }
372
373
        return self::make(self::IMMUTABLE, self::LN_2, 100);
374
375
    }
376
377
    /**
378
     * @param int|null $scale
379
     *
380
     * @return ImmutableDecimal
381
     * @throws IntegrityConstraint
382
     */
383
    public static function makeOne(?int $scale = null): ImmutableDecimal
384
    {
385
        return self::make(self::IMMUTABLE, 1, $scale);
386
    }
387
388
    /**
389
     * @param int|null $scale
390
     *
391
     * @return ImmutableDecimal
392
     * @throws IntegrityConstraint
393
     */
394
    public static function makeZero(?int $scale = null): ImmutableDecimal
395
    {
396
        return self::make(self::IMMUTABLE, 0, $scale);
397
    }
398
399
}