Passed
Pull Request — dev (#56)
by Jordan
08:42
created

Numbers::make()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 8.512

Importance

Changes 0
Metric Value
cc 8
eloc 19
c 0
b 0
f 0
nc 12
nop 4
dl 0
loc 35
ccs 16
cts 20
cp 0.8
crap 8.512
rs 8.4444
1
<?php
2
3
namespace Samsara\Fermat;
4
5
use ReflectionException;
6
use Samsara\Exceptions\UsageError\IntegrityConstraint;
7
use Samsara\Fermat\Provider\ConstantProvider;
8
use Samsara\Fermat\Types\Base\Interfaces\Coordinates\CoordinateInterface;
9
use Samsara\Fermat\Types\Base\Interfaces\Numbers\DecimalInterface;
10
use Samsara\Fermat\Types\Base\Interfaces\Numbers\FractionInterface;
11
use Samsara\Fermat\Types\Base\Interfaces\Numbers\NumberInterface;
12
use Samsara\Fermat\Types\Base\Selectable;
13
use Samsara\Fermat\Types\Fraction;
14
use Samsara\Fermat\Values\Geometry\CoordinateSystems\CartesianCoordinate;
15
use Samsara\Fermat\Values\ImmutableFraction;
16
use Samsara\Fermat\Values\ImmutableDecimal;
17
use Samsara\Fermat\Values\MutableFraction;
18
use Samsara\Fermat\Values\MutableDecimal;
19
20
class Numbers
21
{
22
23
    public const MUTABLE = MutableDecimal::class;
24
    public const IMMUTABLE = ImmutableDecimal::class;
25
    public const MUTABLE_FRACTION = MutableFraction::class;
26
    public const IMMUTABLE_FRACTION = ImmutableFraction::class;
27
    public const CARTESIAN_COORDINATE = CartesianCoordinate::class;
28
    /* 105 digits after decimal, which is going to be overkill in almost all places */
29
    public const PI = '3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679';
30
    /* Tau (2pi) to 100 digits */
31
    public const TAU = '6.283185307179586476925286766559005768394338798750211641949889184615632812572417997256069650684234136';
32
    /* Euler's Number to 100 digits */
33
    public const E = '2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427';
34
    /* Golden Ratio to 100 digits */
35
    public const GOLDEN_RATIO = '1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137';
36
    /* Natural log of 10 to 100 digits */
37
    public const LN_10 = '2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298';
38
    /* The value of i^i */
39
    public const I_POW_I = '0.2078795763507619085469556198349787700338778416317696080751358830554198772854821397886002778654260353';
40
41
    protected static $defaultCalcMode = Selectable::CALC_MODE_PRECISION;
42
43
    /**
44
     * @param $type
45
     * @param $value
46
     * @param int|null $precision
47
     * @param int $base
48
     *
49
     * @return ImmutableDecimal|MutableDecimal|ImmutableFraction|MutableFraction|CartesianCoordinate|NumberInterface|FractionInterface|CoordinateInterface
50
     * @throws IntegrityConstraint
51
     */
52 133
    public static function make($type, $value, $precision = null, $base = 10)
53
    {
54
55 133
        if (is_object($type)) {
56 98
            $type = get_class($type);
57
        }
58
59 133
        if ($type === static::IMMUTABLE) {
60 133
            return new ImmutableDecimal(trim($value), $precision, $base);
61
        }
62
63 16
        if ($type === static::MUTABLE) {
64 7
            return new MutableDecimal(trim($value), $precision, $base);
65
        }
66
67 12
        if ($type === static::IMMUTABLE_FRACTION) {
68 11
            return self::makeFractionFromString($type, $value, $base);
69
        }
70
71 4
        if ($type === static::MUTABLE_FRACTION) {
72 3
            return self::makeFractionFromString($type, $value, $base);
73
        }
74
75 1
        if ($type === static::CARTESIAN_COORDINATE && is_array($value)) {
76 1
            $x = $value[0];
77 1
            $y = $value[1] ?? null;
78 1
            $z = $value[2] ?? null;
79
80 1
            return new CartesianCoordinate($x, $y, $z);
81
        }
82
83
        throw new IntegrityConstraint(
84
            '$type must be an implemented concrete class that is supported',
85
            'Provide a type that implements NumberInterface or CoordinateInterface (the Numbers class contains constants for the built in ones)',
86
            'The $type argument was not an implementation of NumberInterface or CoordinateInterface'
87
        );
88
    }
89
90
    /**
91
     * @param $type
92
     * @param $value
93
     * @param int|null $precision
94
     * @param int $base
95
     *
96
     * @return NumberInterface
97
     * @throws IntegrityConstraint
98
     */
99 1
    public static function makeFromBase10($type, $value, $precision = null, $base = 10): NumberInterface
100
    {
101
        /**
102
         * @var ImmutableDecimal|MutableDecimal
103
         */
104 1
        $number = self::make($type, $value, $precision, 10);
105
106 1
        return $number->convertToBase($base);
0 ignored issues
show
Bug introduced by
The method convertToBase() does not exist on Samsara\Fermat\Values\Ge...ems\CartesianCoordinate. ( Ignorable by Annotation )

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

106
        return $number->/** @scrutinizer ignore-call */ convertToBase($base);

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...
Bug introduced by
The method convertToBase() does not exist on Samsara\Fermat\Values\MutableFraction. ( Ignorable by Annotation )

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

106
        return $number->/** @scrutinizer ignore-call */ convertToBase($base);

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...
Bug introduced by
The method convertToBase() does not exist on Samsara\Fermat\Values\ImmutableFraction. ( Ignorable by Annotation )

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

106
        return $number->/** @scrutinizer ignore-call */ convertToBase($base);

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...
107
    }
108
109
    /**
110
     * @param string|object $type
111
     * @param int|float|string|array|NumberInterface|DecimalInterface|FractionInterface $value
112
     * @param int|null $precision
113
     * @param int $base
114
     *
115
     * @return ImmutableDecimal|MutableDecimal|NumberInterface|ImmutableDecimal[]|MutableDecimal[]|NumberInterface[]
116
     * @throws IntegrityConstraint
117
     */
118 121
    public static function makeOrDont($type, $value, $precision = null, $base = 10)
119
    {
120
121 121
        if (is_object($value)) {
122 97
            if ($value instanceof $type) {
123 97
                return $value;
124
            }
125
126 4
            if ($value instanceof NumberInterface) {
0 ignored issues
show
introduced by
$value is always a sub-type of Samsara\Fermat\Types\Bas...Numbers\NumberInterface.
Loading history...
127 4
                return static::make($type, $value->getValue(), $precision, $base);
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on Samsara\Fermat\Types\Bas...Numbers\NumberInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Samsara\Fermat\Types\Bas...Numbers\NumberInterface. ( Ignorable by Annotation )

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

127
                return static::make($type, $value->/** @scrutinizer ignore-call */ getValue(), $precision, $base);
Loading history...
128
            }
129 111
        } elseif (is_array($value)) {
130 14
            $newInput = [];
131
132 14
            foreach ($value as $key => $item) {
133 14
                $newInput[$key] = static::makeOrDont($type, $item, $precision, $base);
134
            }
135
136 14
            return $newInput;
137 111
        } elseif (is_string($value) || is_int($value) || is_float($value)) {
0 ignored issues
show
introduced by
The condition is_float($value) is always true.
Loading history...
138 111
            $isImaginary = strpos($value, 'i') !== false;
139
140 111
            if (is_numeric($value) || $isImaginary) {
141 111
                return static::make($type, $value, $precision, $base);
142
            }
143
        }
144
145
        throw new IntegrityConstraint(
146
            '$input must be an int, float, numeric string, or an implementation of NumberInterface',
147
            'Provide any of the MANY valid inputs',
148
            'The $input argument was not numeric or an implementation of NumberInterface. Given value: '.$value
149
        );
150
151
    }
152
153
    /**
154
     * @param string $type
155
     * @param string $value
156
     * @param int $base
157
     *
158
     * @return FractionInterface|ImmutableFraction|MutableFraction
159
     * @throws IntegrityConstraint
160
     */
161 11
    public static function makeFractionFromString(string $type, string $value, int $base = 10): FractionInterface
162
    {
163 11
        $parts = explode('/', $value);
164
165 11
        if (count($parts) > 2) {
166
            throw new IntegrityConstraint(
167
                'Only one division symbol (/) can be used',
168
                'Change the calling code to not provide more than one division symbol',
169
                'makeFractionFromString needs either one or zero division symbols in the $value argument; '.$value.' given'
170
            );
171
        }
172
173
        /** @var ImmutableDecimal $numerator */
174 11
        $numerator = self::make(self::IMMUTABLE, trim(ltrim($parts[0])))->round();
175
        /** @var ImmutableDecimal $denominator */
176 11
        $denominator = isset($parts[1]) ? self::make(self::IMMUTABLE, trim(ltrim($parts[1])))->round() : self::makeOne();
177
178 11
        if ($type === self::IMMUTABLE_FRACTION) {
179 11
            return new ImmutableFraction($numerator, $denominator, $base);
180
        }
181
182 3
        if ($type === self::MUTABLE_FRACTION) {
183 3
            return new MutableFraction($numerator, $denominator, $base);
184
        }
185
186
        throw new IntegrityConstraint(
187
            'Type must be an implementation of FractionInterface',
188
            'Alter to calling code to use the correct type',
189
            'makeFractionFromString can only make objects which implement the FractionInterface; '.$type.' given'
190
        );
191
    }
192
193
    /**
194
     * @param int|null $precision
195
     *
196
     * @return NumberInterface
197
     * @throws IntegrityConstraint
198
     */
199 34
    public static function makePi(int $precision = null)
200
    {
201
202 34
        if (!is_null($precision)) {
203 26
            if ($precision > 100 || $precision < 1) {
204 1
                throw new IntegrityConstraint(
205 1
                    '$precision must be between 1 and 100 inclusive',
206 1
                    'Provide a precision within range',
207 1
                    'The PI constant cannot have a precision higher than the constant stored (100)'
208
                );
209
            }
210
211 26
            return self::make(self::IMMUTABLE, self::PI, $precision)->truncateToPrecision($precision);
212
        }
213
214 23
        return self::make(self::IMMUTABLE, self::PI, 100);
215
216
    }
217
218
    /**
219
     * @param int|null $precision
220
     *
221
     * @return NumberInterface
222
     * @throws IntegrityConstraint
223
     */
224 23
    public static function makeTau($precision = null)
225
    {
226 23
        if (!is_null($precision)) {
227 10
            if ($precision > 100 || $precision < 1) {
228 1
                throw new IntegrityConstraint(
229 1
                    '$precision must be between 1 and 100 inclusive',
230 1
                    'Provide a precision within range',
231 1
                    'The TAU constant cannot have a precision higher than the constant stored (100)'
232
                );
233
            }
234
235 10
            return self::make(self::IMMUTABLE, self::TAU, $precision)->truncateToPrecision($precision);
0 ignored issues
show
Bug introduced by
The method truncateToPrecision() does not exist on Samsara\Fermat\Values\ImmutableFraction. ( Ignorable by Annotation )

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

235
            return self::make(self::IMMUTABLE, self::TAU, $precision)->/** @scrutinizer ignore-call */ truncateToPrecision($precision);

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...
Bug introduced by
The method truncateToPrecision() does not exist on Samsara\Fermat\Values\MutableFraction. ( Ignorable by Annotation )

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

235
            return self::make(self::IMMUTABLE, self::TAU, $precision)->/** @scrutinizer ignore-call */ truncateToPrecision($precision);

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...
236
        }
237
238 23
        return self::make(self::IMMUTABLE, self::TAU, 100);
239
    }
240
241
    /**
242
     * @param int|null $precision
243
     *
244
     * @return NumberInterface
245
     * @throws IntegrityConstraint
246
     */
247 23
    public static function make2Pi($precision = null)
248
    {
249 23
        return self::makeTau($precision);
250
    }
251
252
    /**
253
     * @param int|null $precision
254
     *
255
     * @return NumberInterface
256
     * @throws IntegrityConstraint
257
     */
258 9
    public static function makeE(int $precision = null)
259
    {
260
261 9
        if (!is_null($precision)) {
262 7
            if ($precision < 1) {
263
                throw new IntegrityConstraint(
264
                    '$precision must be at least 1',
265
                    'Provide a precision within range',
266
                    'The E constant cannot have a precision less than 1'
267
                );
268
            }
269
270 7
            if ($precision > 100) {
271
                return self::make(self::IMMUTABLE, ConstantProvider::makeE($precision), $precision);
272
            }
273
274 7
            return self::make(self::IMMUTABLE, self::E, $precision)->truncateToPrecision($precision);
275
        }
276
277 4
        return self::make(self::IMMUTABLE, self::E, 100);
278
279
    }
280
281
    /**
282
     * @param int|null $precision
283
     *
284
     * @return NumberInterface
285
     * @throws IntegrityConstraint
286
     */
287 1
    public static function makeGoldenRatio($precision = null)
288
    {
289
290 1
        if (!is_null($precision)) {
291 1
            if ($precision > 100 || $precision < 1) {
292 1
                throw new IntegrityConstraint(
293 1
                    '$precision must be between 1 and 100 inclusive',
294 1
                    'Provide a precision within range',
295 1
                    'The Golden Ratio constant cannot have a precision higher than the constant stored (100)'
296
                );
297
            }
298
299 1
            return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, $precision)->truncateToPrecision($precision);
300
        }
301
302 1
        return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, 100);
303
304
    }
305
306
    /**
307
     * @param int|null $precision
308
     *
309
     * @return NumberInterface
310
     * @throws IntegrityConstraint
311
     */
312 2
    public static function makeNaturalLog10($precision = null)
313
    {
314
315 2
        if (!is_null($precision)) {
316 1
            if ($precision > 100 || $precision < 1) {
317 1
                throw new IntegrityConstraint(
318 1
                    '$precision must be between 1 and 100 inclusive',
319 1
                    'Provide a precision within range',
320 1
                    'The natural log of 10 constant cannot have a precision higher than the constant stored (100)'
321
                );
322
            }
323
324 1
            return self::make(self::IMMUTABLE, self::LN_10, $precision)->truncateToPrecision($precision);
325
        }
326
327 2
        return self::make(self::IMMUTABLE, self::LN_10, 100);
328
329
    }
330
331
    /**
332
     * @param int|null $precision
333
     *
334
     * @return ImmutableDecimal
335
     * @throws IntegrityConstraint
336
     */
337 63
    public static function makeOne($precision = null)
338
    {
339 63
        return self::make(self::IMMUTABLE, 1, $precision);
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::make(self::IMMUTABLE, 1, $precision) also could return the type Samsara\Fermat\Values\Im...\Values\MutableFraction which is incompatible with the documented return type Samsara\Fermat\Values\ImmutableDecimal.
Loading history...
340
    }
341
342
    /**
343
     * @param int|null $precision
344
     *
345
     * @return ImmutableDecimal
346
     * @throws IntegrityConstraint
347
     */
348 58
    public static function makeZero($precision = null)
349
    {
350 58
        return self::make(self::IMMUTABLE, 0, $precision);
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::make(self::IMMUTABLE, 0, $precision) also could return the type Samsara\Fermat\Values\Im...\Values\MutableFraction which is incompatible with the documented return type Samsara\Fermat\Values\ImmutableDecimal.
Loading history...
351
    }
352
353 139
    public static function getDefaultCalcMode(): int
354
    {
355 139
        return self::$defaultCalcMode;
356
    }
357
358
    public static function setDefaultCalcMode(int $mode): void
359
    {
360
        self::$defaultCalcMode = $mode;
361
    }
362
363
}