Test Failed
Pull Request — dev (#56)
by Jordan
12:06
created

Numbers::makeFractionFromString()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 6.2017

Importance

Changes 0
Metric Value
cc 5
eloc 16
c 0
b 0
f 0
nc 7
nop 3
dl 0
loc 29
rs 9.4222
ccs 7
cts 11
cp 0.6364
crap 6.2017
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 133
     * @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 98
    {
54
55
        if (is_object($type)) {
56 133
            $type = get_class($type);
57 133
        }
58
59
        if ($type === static::IMMUTABLE) {
60 16
            return new ImmutableDecimal(trim($value), $precision, $base);
61 7
        }
62
63
        if ($type === static::MUTABLE) {
64 12
            return new MutableDecimal(trim($value), $precision, $base);
65 11
        }
66
67
        if ($type === static::IMMUTABLE_FRACTION) {
68 4
            return self::makeFractionFromString($type, $value, $base);
69 3
        }
70
71
        if ($type === static::MUTABLE_FRACTION) {
72 1
            return self::makeFractionFromString($type, $value, $base);
73 1
        }
74 1
75 1
        if ($type === static::CARTESIAN_COORDINATE && is_array($value)) {
76
            $x = $value[0];
77 1
            $y = $value[1] ?? null;
78
            $z = $value[2] ?? null;
79
80
            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
    public static function makeFromBase10($type, $value, $precision = null, $base = 10): NumberInterface
100
    {
101
        /**
102
         * @var ImmutableDecimal|MutableDecimal
103
         */
104
        $number = self::make($type, $value, $precision, 10);
105
106
        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
    public static function makeOrDont($type, $value, $precision = null, $base = 10)
119
    {
120
121
        if (is_object($value)) {
122
            if ($value instanceof $type) {
123
                return $value;
124
            }
125
126
            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
                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 1
        } elseif (is_array($value)) {
130
            $newInput = [];
131
132
            foreach ($value as $key => $item) {
133
                $newInput[$key] = static::makeOrDont($type, $item, $precision, $base);
134 1
            }
135
136 1
            return $newInput;
137
        } 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
            $isImaginary = strpos($value, 'i') !== false;
139
140
            if (is_numeric($value) || $isImaginary) {
141
                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 121
            'The $input argument was not numeric or an implementation of NumberInterface. Given value: '.$value
149
        );
150
151 121
    }
152 97
153 97
    /**
154
     * @param string $type
155
     * @param string $value
156 4
     * @param int $base
157 4
     *
158
     * @return FractionInterface|ImmutableFraction|MutableFraction
159 111
     * @throws IntegrityConstraint
160 14
     */
161
    public static function makeFractionFromString(string $type, string $value, int $base = 10): FractionInterface
162 14
    {
163 14
        $parts = explode('/', $value);
164
165
        if (count($parts) > 2) {
166 14
            throw new IntegrityConstraint(
167 111
                'Only one division symbol (/) can be used',
168 111
                '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 111
            );
171 111
        }
172
173
        /** @var ImmutableDecimal $numerator */
174
        $numerator = self::make(self::IMMUTABLE, trim(ltrim($parts[0])))->round();
175
        /** @var ImmutableDecimal $denominator */
176
        $denominator = isset($parts[1]) ? self::make(self::IMMUTABLE, trim(ltrim($parts[1])))->round() : self::makeOne();
177
178
        if ($type === self::IMMUTABLE_FRACTION) {
179
            return new ImmutableFraction($numerator, $denominator, $base);
180
        }
181
182
        if ($type === self::MUTABLE_FRACTION) {
183
            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 11
    }
192
193 11
    /**
194
     * @param int|null $precision
195 11
     *
196
     * @return NumberInterface
197
     * @throws IntegrityConstraint
198
     */
199
    public static function makePi(int $precision = null)
200
    {
201
202
        if (!is_null($precision)) {
203
            if ($precision > 100 || $precision < 1) {
204 11
                throw new IntegrityConstraint(
205
                    '$precision must be between 1 and 100 inclusive',
206 11
                    'Provide a precision within range',
207
                    'The PI constant cannot have a precision higher than the constant stored (100)'
208 11
                );
209 11
            }
210
211
            return self::make(self::IMMUTABLE, self::PI, $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

211
            return self::make(self::IMMUTABLE, self::PI, $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

211
            return self::make(self::IMMUTABLE, self::PI, $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...
212 3
        }
213 3
214
        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
    public static function makeTau($precision = null)
225
    {
226
        if (!is_null($precision)) {
227
            if ($precision > 100 || $precision < 1) {
228
                throw new IntegrityConstraint(
229
                    '$precision must be between 1 and 100 inclusive',
230
                    'Provide a precision within range',
231
                    'The TAU constant cannot have a precision higher than the constant stored (100)'
232
                );
233
            }
234
235
            return self::make(self::IMMUTABLE, self::TAU, $precision)->truncateToPrecision($precision);
236
        }
237
238
        return self::make(self::IMMUTABLE, self::TAU, 100);
239
    }
240
241
    /**
242 34
     * @param int|null $precision
243
     *
244
     * @return NumberInterface
245 34
     * @throws IntegrityConstraint
246 26
     */
247 1
    public static function make2Pi($precision = null)
248 1
    {
249 1
        return self::makeTau($precision);
250 1
    }
251
252
    /**
253
     * @param int|null $precision
254 26
     *
255
     * @return NumberInterface
256
     * @throws IntegrityConstraint
257 23
     */
258
    public static function makeE(int $precision = null)
259
    {
260
261
        if (!is_null($precision)) {
262
            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 23
            }
269
270 23
            if ($precision > 100) {
271 10
                return self::make(self::IMMUTABLE, ConstantProvider::makeE($precision), $precision);
272 1
            }
273 1
274 1
            return self::make(self::IMMUTABLE, self::E, $precision)->truncateToPrecision($precision);
275 1
        }
276
277
        return self::make(self::IMMUTABLE, self::E, 100);
278
279 10
    }
280
281
    /**
282 23
     * @param int|null $precision
283
     *
284
     * @return NumberInterface
285
     * @throws IntegrityConstraint
286
     */
287
    public static function makeGoldenRatio($precision = null)
288
    {
289
290
        if (!is_null($precision)) {
291
            if ($precision > 100 || $precision < 1) {
292 23
                throw new IntegrityConstraint(
293
                    '$precision must be between 1 and 100 inclusive',
294 23
                    'Provide a precision within range',
295
                    'The Golden Ratio constant cannot have a precision higher than the constant stored (100)'
296
                );
297
            }
298
299
            return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, $precision)->truncateToPrecision($precision);
300
        }
301
302
        return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, 100);
303
304 9
    }
305
306
    /**
307 9
     * @param int|null $precision
308 7
     *
309 1
     * @return NumberInterface
310 1
     * @throws IntegrityConstraint
311 1
     */
312 1
    public static function makeNaturalLog10($precision = null)
313
    {
314
315
        if (!is_null($precision)) {
316 7
            if ($precision > 100 || $precision < 1) {
317
                throw new IntegrityConstraint(
318
                    '$precision must be between 1 and 100 inclusive',
319 4
                    'Provide a precision within range',
320
                    'The natural log of 10 constant cannot have a precision higher than the constant stored (100)'
321
                );
322
            }
323
324
            return self::make(self::IMMUTABLE, self::LN_10, $precision)->truncateToPrecision($precision);
325
        }
326
327
        return self::make(self::IMMUTABLE, self::LN_10, 100);
328
329
    }
330 1
331
    /**
332
     * @param int|null $precision
333 1
     *
334 1
     * @return ImmutableDecimal
335 1
     * @throws IntegrityConstraint
336 1
     */
337 1
    public static function makeOne($precision = null)
338 1
    {
339
        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 1
    /**
343
     * @param int|null $precision
344
     *
345 1
     * @return ImmutableDecimal
346
     * @throws IntegrityConstraint
347
     */
348
    public static function makeZero($precision = null)
349
    {
350
        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
    public static function getDefaultCalcMode(): int
354
    {
355
        return self::$defaultCalcMode;
356 2
    }
357
358
    public static function setDefaultCalcMode(int $mode): void
359 2
    {
360 1
        self::$defaultCalcMode = $mode;
361 1
    }
362
363
}