Passed
Push — dev ( b7d069...4bfcb0 )
by Jordan
02:52
created

Numbers::makeFractionFromString()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 24
nc 9
nop 3
dl 0
loc 38
ccs 0
cts 24
cp 0
crap 56
rs 6.7272
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\CoordinateInterface;
7
use Samsara\Fermat\Types\Base\DecimalInterface;
8
use Samsara\Fermat\Types\Base\FractionInterface;
9
use Samsara\Fermat\Types\Fraction;
10
use Samsara\Fermat\Values\CartesianCoordinate;
11
use Samsara\Fermat\Values\ImmutableFraction;
12
use Samsara\Fermat\Values\ImmutableNumber;
13
use Samsara\Fermat\Values\MutableFraction;
14
use Samsara\Fermat\Values\MutableNumber;
15
use Samsara\Fermat\Types\Base\NumberInterface;
16
17
class Numbers
18
{
19
20
    const MUTABLE = MutableNumber::class;
21
    const IMMUTABLE = ImmutableNumber::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
36
    /**
37
     * @param $type
38
     * @param $value
39
     * @param int|null $precision
40
     * @param int $base
41
     *
42
     * @throws IntegrityConstraint
43
     * @return ImmutableNumber|MutableNumber|ImmutableFraction|MutableFraction|CartesianCoordinate|NumberInterface|FractionInterface|CoordinateInterface
44
     */
45 23
    public static function make($type, $value, $precision = null, $base = 10)
46
    {
47
48 23
        if (is_object($type)) {
49 18
            $type = get_class($type);
50
        }
51
52 23
        if ($type == static::IMMUTABLE) {
53 23
            return new ImmutableNumber(trim($value), $precision, $base);
54 1
        } elseif ($type == static::MUTABLE) {
55 1
            return new MutableNumber(trim($value), $precision, $base);
56
        } elseif ($type == static::IMMUTABLE_FRACTION) {
57
            return self::makeFractionFromString($type, $value, $base);
58
        } elseif ($type == static::MUTABLE_FRACTION) {
59
            return self::makeFractionFromString($type, $value, $base);
60
        } elseif ($type == static::CARTESIAN_COORDINATE && is_array($value)) {
61
            return new CartesianCoordinate($value);
62
        } else {
63
            $reflector = new \ReflectionClass($type);
64
65
            if ($reflector->implementsInterface(FractionInterface::class) && $reflector->isSubclassOf(Fraction::class)) {
66
                return Numbers::makeFractionFromString($reflector->getName(), $value, $base);
67
            }
68
69
            if ($reflector->implementsInterface(CoordinateInterface::class) && is_array($value)) {
70
                /** @var CoordinateInterface $customCoordinate */
71
                $customCoordinate = $reflector->newInstance([
72
                    $value
73
                ]);
74
                return $customCoordinate;
75
            }
76
77
            if ($reflector->implementsInterface(NumberInterface::class)) {
78
                /** @var NumberInterface $customNumber */
79
                $customNumber = $reflector->newInstance([
80
                    trim($value),
81
                    $precision,
82
                    $base
83
                ]);
84
                return $customNumber;
85
            }
86
87
            if ($reflector->implementsInterface(CoordinateInterface::class) && !is_array($value)) {
88
                throw new IntegrityConstraint(
89
                    'The $value for a CoordinateInterface must be an array',
90
                    'Provide an array for the $value',
91
                    'A CoordinateInterface expects the value to be an array of axes and values'
92
                );
93
            }
94
        }
95
96
        throw new IntegrityConstraint(
97
            '$type must be an implementation of NumberInterface or CoordinateInterface',
98
            'Provide a type that implements NumberInterface or CoordinateInterface (the Numbers class contains constants for the built in ones)',
99
            'The $type argument was not an implementation of NumberInterface or CoordinateInterface'
100
        );
101
    }
102
103
    /**
104
     * @param $type
105
     * @param $value
106
     * @param int|null $precision
107
     * @param int $base
108
     * @return NumberInterface
109
     */
110
    public static function makeFromBase10($type, $value, $precision = null, $base = 10)
111
    {
112
        /**
113
         * @var ImmutableNumber|MutableNumber
114
         */
115
        $number = self::make($type, $value, $precision, 10);
116
117
        return $number->convertToBase($base);
0 ignored issues
show
Bug introduced by
The method convertToBase() does not exist on Samsara\Fermat\Types\Base\CoordinateInterface. ( Ignorable by Annotation )

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

117
        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\CartesianCoordinate. ( Ignorable by Annotation )

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

117
        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 Best Practice introduced by
The expression return $number->convertToBase($base) also could return the type Samsara\Fermat\Types\Fra...s\Base\DecimalInterface which is incompatible with the documented return type Samsara\Fermat\Types\Base\NumberInterface.
Loading history...
Bug introduced by
The method convertToBase() does not exist on Samsara\Fermat\Types\Base\FractionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Samsara\Fermat\Types\Base\FractionInterface. ( Ignorable by Annotation )

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

117
        return $number->/** @scrutinizer ignore-call */ convertToBase($base);
Loading history...
118
    }
119
120
    /**
121
     * @param $type
122
     * @param int|float|string|NumberInterface|DecimalInterface|FractionInterface $value
123
     * @param int|null $precision
124
     * @param int $base
125
     *
126
     * @throws IntegrityConstraint
127
     * @return ImmutableNumber|MutableNumber|NumberInterface|ImmutableNumber[]|MutableNumber[]|NumberInterface[]
128
     */
129 25
    public static function makeOrDont($type, $value, $precision = null, $base = 10)
130
    {
131
132 25
        if (is_object($value)) {
133 23
            $reflector = new \ReflectionClass($value);
134
135 23
            if ($value instanceof $type) {
136 23
                return $value;
137
            }
138
139 1
            if ($reflector->implementsInterface(NumberInterface::class)) {
140 1
                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\Base\FractionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Samsara\Fermat\Types\Base\FractionInterface. ( Ignorable by Annotation )

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

140
                return static::make($type, $value->/** @scrutinizer ignore-call */ getValue(), $precision, $base);
Loading history...
Bug introduced by
The method getValue() does not exist on Samsara\Fermat\Types\Base\DecimalInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Samsara\Fermat\Types\Base\DecimalInterface. ( Ignorable by Annotation )

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

140
                return static::make($type, $value->/** @scrutinizer ignore-call */ getValue(), $precision, $base);
Loading history...
141
            }
142 23
        } elseif (is_numeric($value)) {
143 23
            return static::make($type, $value, $precision, $base);
144
        } elseif (is_array($value)) {
145
            $newInput = [];
146
            
147
            foreach ($value as $key => $item) {
148
                $newInput[$key] = static::makeOrDont($type, $item, $precision, $base);
149
            }
150
151
            return $newInput;
152
        }
153
154
        throw new IntegrityConstraint(
155
            '$input must be an int, float, numeric string, or an implementation of NumberInterface',
156
            'Provide any of the MANY valid inputs',
157
            'The $input argument was not numeric or an implementation of NumberInterface.'
158
        );
159
160
    }
161
162
    /**
163
     * @param     $type
164
     * @param     $value
165
     * @param int $base
166
     *
167
     * @return FractionInterface|ImmutableFraction|MutableFraction
168
     * @throws IntegrityConstraint
169
     */
170
    public static function makeFractionFromString($type, $value, $base = 10)
171
    {
172
        $parts = explode('/', $value);
173
174
        if (count($parts) > 2) {
175
            throw new IntegrityConstraint(
176
                'Only one division symbol (/) can be used',
177
                'Change the calling code to not provide more than one division symbol',
178
                'makeFractionFromString needs either one or zero division symbols in the $value argument; '.$value.' given'
179
            );
180
        }
181
182
        /** @var ImmutableNumber $numerator */
183
        $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\Types\Base\FractionInterface. ( Ignorable by Annotation )

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

183
        $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\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

183
        $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\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

183
        $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\Base\NumberInterface. It seems like you code against a sub-type of Samsara\Fermat\Types\Base\NumberInterface such as Samsara\Fermat\Values\ImmutableNumber or Samsara\Fermat\Values\MutableNumber. ( Ignorable by Annotation )

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

183
        $numerator = Numbers::make(Numbers::IMMUTABLE, trim(ltrim($parts[0])))->/** @scrutinizer ignore-call */ round();
Loading history...
184
        /** @var ImmutableNumber $denominator */
185
        $denominator = isset($parts[1]) ? Numbers::make(Numbers::IMMUTABLE, trim(ltrim($parts[1])))->round() : Numbers::makeOne();
186
187
        if ($type == self::IMMUTABLE_FRACTION) {
188
            return new ImmutableFraction($numerator, $denominator, $base);
189
        } elseif ($type == self::MUTABLE_FRACTION) {
190
            return new MutableFraction($numerator, $denominator, $base);
191
        } else {
192
            $reflector = new \ReflectionClass($type);
193
194
            if ($reflector->implementsInterface(FractionInterface::class) && $reflector->isSubclassOf(Fraction::class)) {
195
                /** @var FractionInterface|Fraction $customFraction */
196
                $customFraction = $reflector->newInstance([
197
                    $numerator,
198
                    $denominator,
199
                    $base
200
                ]);
201
                return $customFraction;
202
            }
203
204
            throw new IntegrityConstraint(
205
                'Type must be an implementation of FractionInterface',
206
                'Alter to calling code to use the correct type',
207
                'makeFractionFromString can only make objects which implement the FractionInterface; '.$type.' given'
208
            );
209
        }
210
    }
211
212
    /**
213
     * @param int|null $precision
214
     *
215
     * @throws IntegrityConstraint
216
     * @return NumberInterface
217
     */
218 5
    public static function makePi($precision = null)
219
    {
220
        
221 5
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
222
            throw new IntegrityConstraint(
223
                '$precision must be between 1 and 100 inclusive',
224
                'Provide a precision within range',
225
                'The PI constant cannot have a precision higher than the constant stored (100)'
226
            );
227
        }
228
        
229 5
        if (!is_null($precision)) {
230
            return self::make(self::IMMUTABLE, self::PI, $precision)->roundToPrecision($precision);
0 ignored issues
show
Bug introduced by
The method roundToPrecision() 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

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

230
            return self::make(self::IMMUTABLE, self::PI, $precision)->/** @scrutinizer ignore-call */ roundToPrecision($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 roundToPrecision() does not exist on Samsara\Fermat\Types\Base\FractionInterface. ( Ignorable by Annotation )

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

230
            return self::make(self::IMMUTABLE, self::PI, $precision)->/** @scrutinizer ignore-call */ roundToPrecision($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 Best Practice introduced by
The expression return self::make(self::...ToPrecision($precision) also could return the type Samsara\Fermat\Types\Bas...s\Base\DecimalInterface which is incompatible with the documented return type Samsara\Fermat\Types\Base\NumberInterface.
Loading history...
Bug introduced by
The method roundToPrecision() does not exist on Samsara\Fermat\Types\Base\NumberInterface. It seems like you code against a sub-type of Samsara\Fermat\Types\Base\NumberInterface such as Samsara\Fermat\Values\ImmutableNumber or Samsara\Fermat\Values\MutableNumber. ( Ignorable by Annotation )

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

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