Failed Conditions
Pull Request — master (#47)
by Jordan
07:14 queued 03:12
created

Numbers::makeFractionFromString()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 18.9628

Importance

Changes 0
Metric Value
cc 7
eloc 24
nc 9
nop 3
dl 0
loc 38
ccs 9
cts 24
cp 0.375
crap 18.9628
rs 8.6026
c 0
b 0
f 0
1
<?php
2
3
namespace Samsara\Fermat;
4
5
use Samsara\Exceptions\UsageError\IntegrityConstraint;
6
use Samsara\Fermat\Types\Base\Interfaces\Coordinates\CoordinateInterface;
7
use Samsara\Fermat\Types\Base\Interfaces\Numbers\DecimalInterface;
8
use Samsara\Fermat\Types\Base\Interfaces\Numbers\FractionInterface;
9
use Samsara\Fermat\Types\Base\Interfaces\Numbers\NumberInterface;
10
use Samsara\Fermat\Types\Fraction;
11
use Samsara\Fermat\Values\Geometry\CoordinateSystems\CartesianCoordinate;
12
use Samsara\Fermat\Values\ImmutableFraction;
13
use Samsara\Fermat\Values\ImmutableDecimal;
14
use Samsara\Fermat\Values\MutableFraction;
15
use Samsara\Fermat\Values\MutableDecimal;
16
17
class Numbers
18
{
19
20
    const MUTABLE = MutableDecimal::class;
21
    const IMMUTABLE = ImmutableDecimal::class;
22
    const MUTABLE_FRACTION = MutableFraction::class;
23
    const IMMUTABLE_FRACTION = ImmutableFraction::class;
24
    const CARTESIAN_COORDINATE = CartesianCoordinate::class;
25
    /* 105 digits after decimal, which is going to be overkill in almost all places */
26
    const PI = '3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679';
27
    /* Tau (2pi) to 100 digits */
28
    const TAU = '6.283185307179586476925286766559005768394338798750211641949889184615632812572417997256069650684234136';
29
    /* Euler's Number to 100 digits */
30
    const E = '2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427';
31
    /* Golden Ratio to 100 digits */
32
    const GOLDEN_RATIO = '1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137';
33
    /* Natural log of 10 to 100 digits */
34
    const LN_10 = '2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298';
35
    /* The value of i^i */
36
    const I_POW_I = '0.2078795763507619085469556198349787700338778416317696080751358830554198772854821397886002778654260353';
37
38
    /**
39
     * @param $type
40
     * @param $value
41
     * @param int|null $precision
42
     * @param int $base
43
     *
44
     * @return ImmutableDecimal|MutableDecimal|ImmutableFraction|MutableFraction|CartesianCoordinate|NumberInterface|FractionInterface|CoordinateInterface
45
     *@throws IntegrityConstraint
46
     */
47 77
    public static function make($type, $value, $precision = null, $base = 10)
48
    {
49
50 77
        if (is_object($type)) {
51 52
            $type = get_class($type);
52
        }
53
54 77
        if ($type == static::IMMUTABLE) {
55 77
            return new ImmutableDecimal(trim($value), $precision, $base);
56 6
        } elseif ($type == static::MUTABLE) {
57 5
            return new MutableDecimal(trim($value), $precision, $base);
58 4
        } elseif ($type == static::IMMUTABLE_FRACTION) {
59 3
            return self::makeFractionFromString($type, $value, $base);
60 4
        } elseif ($type == static::MUTABLE_FRACTION) {
61 3
            return self::makeFractionFromString($type, $value, $base);
62 1
        } elseif ($type == static::CARTESIAN_COORDINATE && is_array($value)) {
63 1
            $x = $value[0];
64
65 1
            if (isset($value[1])) {
66 1
                $y = $value[1];
67
            } else {
68
                $y = null;
69
            }
70
71 1
            if (isset($value[2])) {
72
                $z = $value[2];
73
            } else {
74 1
                $z = null;
75
            }
76
77 1
            return new CartesianCoordinate($x, $y, $z);
78
        } else {
79
            $reflector = new \ReflectionClass($type);
80
81
            if ($reflector->implementsInterface(FractionInterface::class) && $reflector->isSubclassOf(Fraction::class)) {
82
                return Numbers::makeFractionFromString($reflector->getName(), $value, $base);
83
            }
84
85
            if ($reflector->implementsInterface(CoordinateInterface::class) && is_array($value)) {
86
                /** @var CoordinateInterface $customCoordinate */
87
                $customCoordinate = $reflector->newInstance([
88
                    $value
89
                ]);
90
                return $customCoordinate;
91
            }
92
93
            if ($reflector->implementsInterface(NumberInterface::class)) {
94
                /** @var NumberInterface $customNumber */
95
                $customNumber = $reflector->newInstance([
96
                    trim($value),
97
                    $precision,
98
                    $base
99
                ]);
100
                return $customNumber;
101
            }
102
103
            if ($reflector->implementsInterface(CoordinateInterface::class) && !is_array($value)) {
104
                throw new IntegrityConstraint(
105
                    'The $value for a CoordinateInterface must be an array',
106
                    'Provide an array for the $value',
107
                    'A CoordinateInterface expects the value to be an array of axes and values'
108
                );
109
            }
110
        }
111
112
        throw new IntegrityConstraint(
113
            '$type must be an implementation of NumberInterface or CoordinateInterface',
114
            'Provide a type that implements NumberInterface or CoordinateInterface (the Numbers class contains constants for the built in ones)',
115
            'The $type argument was not an implementation of NumberInterface or CoordinateInterface'
116
        );
117
    }
118
119
    /**
120
     * @param $type
121
     * @param $value
122
     * @param int|null $precision
123
     * @param int $base
124
     * @return NumberInterface
125
     * @throws IntegrityConstraint
126
     */
127 1
    public static function makeFromBase10($type, $value, $precision = null, $base = 10)
128
    {
129
        /**
130
         * @var ImmutableDecimal|MutableDecimal
131
         */
132 1
        $number = self::make($type, $value, $precision, 10);
133
134 1
        return $number->convertToBase($base);
0 ignored issues
show
Bug introduced by
The method convertToBase() does not exist on Samsara\Fermat\Types\Bas...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

134
        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\Types\Bas...Numbers\NumberInterface. It seems like you code against a sub-type of Samsara\Fermat\Types\Bas...Numbers\NumberInterface such as Samsara\Fermat\Types\Decimal or Samsara\Fermat\Types\Decimal. ( Ignorable by Annotation )

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

134
        return $number->/** @scrutinizer ignore-call */ convertToBase($base);
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

134
        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\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

134
        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

134
        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\Types\Bas...tes\CoordinateInterface. ( Ignorable by Annotation )

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

134
        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...
135
    }
136
137
    /**
138
     * @param $type
139
     * @param int|float|string|NumberInterface|DecimalInterface|FractionInterface $value
140
     * @param int|null $precision
141
     * @param int $base
142
     *
143
     * @return ImmutableDecimal|MutableDecimal|NumberInterface|ImmutableDecimal[]|MutableDecimal[]|NumberInterface[]
144
     * @throws IntegrityConstraint|\ReflectionException
145
     */
146 71
    public static function makeOrDont($type, $value, $precision = null, $base = 10)
147
    {
148
149 71
        if (is_object($value)) {
150 60
            $reflector = new \ReflectionClass($value);
151
152 60
            if ($value instanceof $type) {
153 60
                return $value;
154
            }
155
156 2
            if ($reflector->implementsInterface(NumberInterface::class)) {
157 2
                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

157
                return static::make($type, $value->/** @scrutinizer ignore-call */ getValue(), $precision, $base);
Loading history...
158
            }
159 69
        } elseif (is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
160 7
            $newInput = [];
161
162 7
            foreach ($value as $key => $item) {
163 7
                $newInput[$key] = static::makeOrDont($type, $item, $precision, $base);
164
            }
165
166 7
            return $newInput;
167 69
        } elseif (is_numeric($value)) {
168 69
            return static::make($type, $value, $precision, $base);
169
        }
170
171
        throw new IntegrityConstraint(
172
            '$input must be an int, float, numeric string, or an implementation of NumberInterface',
173
            'Provide any of the MANY valid inputs',
174
            'The $input argument was not numeric or an implementation of NumberInterface.'
175
        );
176
177
    }
178
179
    /**
180
     * @param     $type
181
     * @param     $value
182
     * @param int $base
183
     *
184
     * @return FractionInterface|ImmutableFraction|MutableFraction
185
     * @throws IntegrityConstraint|\ReflectionException
186
     */
187 3
    public static function makeFractionFromString($type, $value, $base = 10)
188
    {
189 3
        $parts = explode('/', $value);
190
191 3
        if (count($parts) > 2) {
192
            throw new IntegrityConstraint(
193
                'Only one division symbol (/) can be used',
194
                'Change the calling code to not provide more than one division symbol',
195
                'makeFractionFromString needs either one or zero division symbols in the $value argument; '.$value.' given'
196
            );
197
        }
198
199
        /** @var ImmutableDecimal $numerator */
200 3
        $numerator = Numbers::make(Numbers::IMMUTABLE, trim(ltrim($parts[0])))->round();
0 ignored issues
show
Bug introduced by
The method round() 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

200
        $numerator = Numbers::make(Numbers::IMMUTABLE, trim(ltrim($parts[0])))->/** @scrutinizer ignore-call */ round();

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 round() does not exist on Samsara\Fermat\Types\Bas...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

200
        $numerator = Numbers::make(Numbers::IMMUTABLE, trim(ltrim($parts[0])))->/** @scrutinizer ignore-call */ round();

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 round() does not exist on Samsara\Fermat\Types\Bas...Numbers\NumberInterface. It seems like you code against a sub-type of Samsara\Fermat\Types\Bas...Numbers\NumberInterface such as Samsara\Fermat\Types\Bas...umbers\DecimalInterface or Samsara\Fermat\Types\Decimal. ( Ignorable by Annotation )

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

200
        $numerator = Numbers::make(Numbers::IMMUTABLE, trim(ltrim($parts[0])))->/** @scrutinizer ignore-call */ round();
Loading history...
Bug introduced by
The method round() 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

200
        $numerator = Numbers::make(Numbers::IMMUTABLE, trim(ltrim($parts[0])))->/** @scrutinizer ignore-call */ round();

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...
201
        /** @var ImmutableDecimal $denominator */
202 3
        $denominator = isset($parts[1]) ? Numbers::make(Numbers::IMMUTABLE, trim(ltrim($parts[1])))->round() : Numbers::makeOne();
203
204 3
        if ($type == self::IMMUTABLE_FRACTION) {
205 3
            return new ImmutableFraction($numerator, $denominator, $base);
206 3
        } elseif ($type == self::MUTABLE_FRACTION) {
207 3
            return new MutableFraction($numerator, $denominator, $base);
208
        } else {
209
            $reflector = new \ReflectionClass($type);
210
211
            if ($reflector->implementsInterface(FractionInterface::class) && $reflector->isSubclassOf(Fraction::class)) {
212
                /** @var FractionInterface|Fraction $customFraction */
213
                $customFraction = $reflector->newInstance([
214
                    $numerator,
215
                    $denominator,
216
                    $base
217
                ]);
218
                return $customFraction;
219
            }
220
221
            throw new IntegrityConstraint(
222
                'Type must be an implementation of FractionInterface',
223
                'Alter to calling code to use the correct type',
224
                'makeFractionFromString can only make objects which implement the FractionInterface; '.$type.' given'
225
            );
226
        }
227
    }
228
229
    /**
230
     * @param int|null $precision
231
     *
232
     * @throws IntegrityConstraint
233
     * @return NumberInterface
234
     */
235 17
    public static function makePi($precision = null)
236
    {
237
        
238 17
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
239 1
            throw new IntegrityConstraint(
240 1
                '$precision must be between 1 and 100 inclusive',
241 1
                'Provide a precision within range',
242 1
                'The PI constant cannot have a precision higher than the constant stored (100)'
243
            );
244
        }
245
        
246 17
        if (!is_null($precision)) {
247 8
            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\Types\Bas...Numbers\NumberInterface. It seems like you code against a sub-type of Samsara\Fermat\Types\Bas...Numbers\NumberInterface such as Samsara\Fermat\Types\Bas...umbers\DecimalInterface or Samsara\Fermat\Types\Decimal. ( Ignorable by Annotation )

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

247
            return self::make(self::IMMUTABLE, self::PI, $precision)->/** @scrutinizer ignore-call */ truncateToPrecision($precision);
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

247
            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\ImmutableFraction. ( Ignorable by Annotation )

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

247
            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\Types\Bas...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

247
            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...
248
        } else {
249 17
            return self::make(self::IMMUTABLE, self::PI, 100);
250
        }
251
        
252
    }
253
254
    /**
255
     * @param int|null $precision
256
     *
257
     * @throws IntegrityConstraint
258
     * @return NumberInterface
259
     */
260 16
    public static function makeTau($precision = null)
261
    {
262 16
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
263 1
            throw new IntegrityConstraint(
264 1
                '$precision must be between 1 and 100 inclusive',
265 1
                'Provide a precision within range',
266 1
                'The TAU constant cannot have a precision higher than the constant stored (100)'
267
            );
268
        }
269
270 16
        if (!is_null($precision)) {
271 2
            return self::make(self::IMMUTABLE, self::TAU, $precision)->truncateToPrecision($precision);
272
        } else {
273 16
            return self::make(self::IMMUTABLE, self::TAU, 100);
274
        }
275
    }
276
277
    /**
278
     * @param int|null $precision
279
     *
280
     * @return NumberInterface
281
     * @throws IntegrityConstraint
282
     */
283 16
    public static function make2Pi($precision = null)
284
    {
285 16
        return self::makeTau($precision);
286
    }
287
288
    /**
289
     * @param int|null $precision
290
     *
291
     * @throws IntegrityConstraint
292
     * @return NumberInterface
293
     */
294 7
    public static function makeE($precision = null)
295
    {
296
297 7
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
298 1
            throw new IntegrityConstraint(
299 1
                '$precision must be between 1 and 100 inclusive',
300 1
                'Provide a precision within range',
301 1
                'The E constant cannot have a precision higher than the constant stored (100)'
302
            );
303
        }
304
305 7
        if (!is_null($precision)) {
306 5
            return self::make(self::IMMUTABLE, self::E, $precision)->truncateToPrecision($precision);
307
        } else {
308 4
            return self::make(self::IMMUTABLE, self::E, 100);
309
        }
310
311
    }
312
313
    /**
314
     * @param int|null $precision
315
     *
316
     * @throws IntegrityConstraint
317
     * @return NumberInterface
318
     */
319 1
    public static function makeGoldenRatio($precision = null)
320
    {
321
322 1
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
323 1
            throw new IntegrityConstraint(
324 1
                '$precision must be between 1 and 100 inclusive',
325 1
                'Provide a precision within range',
326 1
                'The Golden Ratio constant cannot have a precision higher than the constant stored (100)'
327
            );
328
        }
329
330 1
        if (!is_null($precision)) {
331 1
            return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, $precision)->truncateToPrecision($precision);
332
        } else {
333 1
            return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, 100);
334
        }
335
336
    }
337
338
    /**
339
     * @param int|null $precision
340
     *
341
     * @throws IntegrityConstraint
342
     * @return NumberInterface
343
     */
344 2
    public static function makeNaturalLog10($precision = null)
345
    {
346
347 2
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
348 1
            throw new IntegrityConstraint(
349 1
                '$precision must be between 1 and 100 inclusive',
350 1
                'Provide a precision within range',
351 1
                'The natural log of 10 constant cannot have a precision higher than the constant stored (100)'
352
            );
353
        }
354
355 2
        if (!is_null($precision)) {
356 1
            return self::make(self::IMMUTABLE, self::LN_10, $precision)->truncateToPrecision($precision);
357
        } else {
358 2
            return self::make(self::IMMUTABLE, self::LN_10, 100);
359
        }
360
361
    }
362
363
    /**
364
     * @param int|null $precision
365
     *
366
     * @return ImmutableDecimal
367
     * @throws IntegrityConstraint
368
     */
369 29
    public static function makeOne($precision = null)
370
    {
371 29
        return self::make(self::IMMUTABLE, 1, $precision);
372
    }
373
374
    /**
375
     * @param int|null $precision
376
     *
377
     * @return ImmutableDecimal
378
     * @throws IntegrityConstraint
379
     */
380 31
    public static function makeZero($precision = null)
381
    {
382 31
        return self::make(self::IMMUTABLE, 0, $precision);
383
    }
384
385
}