Failed Conditions
Push — dev ( 5f5cb9...8ee5b7 )
by Jordan
03:10
created

Numbers::makeE()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 9
nc 3
nop 1
dl 0
loc 15
ccs 9
cts 9
cp 1
crap 5
rs 9.6111
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 80
    public static function make($type, $value, $precision = null, $base = 10)
48
    {
49
50 80
        if (is_object($type)) {
51 54
            $type = get_class($type);
52
        }
53
54 80
        if ($type == static::IMMUTABLE) {
55 80
            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 74
    public static function makeOrDont($type, $value, $precision = null, $base = 10)
147
    {
148
149 74
        if (is_object($value)) {
150 62
            if ($value instanceof $type) {
151 62
                return $value;
152
            }
153
154 2
            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...
155 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

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

202
        $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

202
        $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

202
        $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

202
        $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...
203
        /** @var ImmutableDecimal $denominator */
204 3
        $denominator = isset($parts[1]) ? Numbers::make(Numbers::IMMUTABLE, trim(ltrim($parts[1])))->round() : Numbers::makeOne();
205
206 3
        if ($type == self::IMMUTABLE_FRACTION) {
207 3
            return new ImmutableFraction($numerator, $denominator, $base);
208 3
        } elseif ($type == self::MUTABLE_FRACTION) {
209 3
            return new MutableFraction($numerator, $denominator, $base);
210
        } else {
211
            $reflector = new \ReflectionClass($type);
212
213
            if ($reflector->implementsInterface(FractionInterface::class) && $reflector->isSubclassOf(Fraction::class)) {
214
                /** @var FractionInterface|Fraction $customFraction */
215
                $customFraction = $reflector->newInstance([
216
                    $numerator,
217
                    $denominator,
218
                    $base
219
                ]);
220
                return $customFraction;
221
            }
222
223
            throw new IntegrityConstraint(
224
                'Type must be an implementation of FractionInterface',
225
                'Alter to calling code to use the correct type',
226
                'makeFractionFromString can only make objects which implement the FractionInterface; '.$type.' given'
227
            );
228
        }
229
    }
230
231
    /**
232
     * @param int|null $precision
233
     *
234
     * @throws IntegrityConstraint
235
     * @return NumberInterface
236
     */
237 19
    public static function makePi($precision = null)
238
    {
239
        
240 19
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
241 1
            throw new IntegrityConstraint(
242 1
                '$precision must be between 1 and 100 inclusive',
243 1
                'Provide a precision within range',
244 1
                'The PI constant cannot have a precision higher than the constant stored (100)'
245
            );
246
        }
247
        
248 19
        if (!is_null($precision)) {
249 10
            return self::make(self::IMMUTABLE, self::PI, $precision)->truncateToPrecision($precision);
250
        } else {
251 19
            return self::make(self::IMMUTABLE, self::PI, 100);
252
        }
253
        
254
    }
255
256
    /**
257
     * @param int|null $precision
258
     *
259
     * @throws IntegrityConstraint
260
     * @return NumberInterface
261
     */
262 18
    public static function makeTau($precision = null)
263
    {
264 18
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
265 1
            throw new IntegrityConstraint(
266 1
                '$precision must be between 1 and 100 inclusive',
267 1
                'Provide a precision within range',
268 1
                'The TAU constant cannot have a precision higher than the constant stored (100)'
269
            );
270
        }
271
272 18
        if (!is_null($precision)) {
273 3
            return self::make(self::IMMUTABLE, self::TAU, $precision)->truncateToPrecision($precision);
274
        } else {
275 18
            return self::make(self::IMMUTABLE, self::TAU, 100);
276
        }
277
    }
278
279
    /**
280
     * @param int|null $precision
281
     *
282
     * @return NumberInterface
283
     * @throws IntegrityConstraint
284
     */
285 18
    public static function make2Pi($precision = null)
286
    {
287 18
        return self::makeTau($precision);
288
    }
289
290
    /**
291
     * @param int|null $precision
292
     *
293
     * @throws IntegrityConstraint
294
     * @return NumberInterface
295
     */
296 7
    public static function makeE($precision = null)
297
    {
298
299 7
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
300 1
            throw new IntegrityConstraint(
301 1
                '$precision must be between 1 and 100 inclusive',
302 1
                'Provide a precision within range',
303 1
                'The E constant cannot have a precision higher than the constant stored (100)'
304
            );
305
        }
306
307 7
        if (!is_null($precision)) {
308 5
            return self::make(self::IMMUTABLE, self::E, $precision)->truncateToPrecision($precision);
309
        } else {
310 4
            return self::make(self::IMMUTABLE, self::E, 100);
311
        }
312
313
    }
314
315
    /**
316
     * @param int|null $precision
317
     *
318
     * @throws IntegrityConstraint
319
     * @return NumberInterface
320
     */
321 1
    public static function makeGoldenRatio($precision = null)
322
    {
323
324 1
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
325 1
            throw new IntegrityConstraint(
326 1
                '$precision must be between 1 and 100 inclusive',
327 1
                'Provide a precision within range',
328 1
                'The Golden Ratio constant cannot have a precision higher than the constant stored (100)'
329
            );
330
        }
331
332 1
        if (!is_null($precision)) {
333 1
            return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, $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

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

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

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

333
            return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, $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...
334
        } else {
335 1
            return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, 100);
336
        }
337
338
    }
339
340
    /**
341
     * @param int|null $precision
342
     *
343
     * @throws IntegrityConstraint
344
     * @return NumberInterface
345
     */
346 2
    public static function makeNaturalLog10($precision = null)
347
    {
348
349 2
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
350 1
            throw new IntegrityConstraint(
351 1
                '$precision must be between 1 and 100 inclusive',
352 1
                'Provide a precision within range',
353 1
                'The natural log of 10 constant cannot have a precision higher than the constant stored (100)'
354
            );
355
        }
356
357 2
        if (!is_null($precision)) {
358 1
            return self::make(self::IMMUTABLE, self::LN_10, $precision)->truncateToPrecision($precision);
359
        } else {
360 2
            return self::make(self::IMMUTABLE, self::LN_10, 100);
361
        }
362
363
    }
364
365
    /**
366
     * @param int|null $precision
367
     *
368
     * @return ImmutableDecimal
369
     * @throws IntegrityConstraint
370
     */
371 31
    public static function makeOne($precision = null)
372
    {
373 31
        return self::make(self::IMMUTABLE, 1, $precision);
374
    }
375
376
    /**
377
     * @param int|null $precision
378
     *
379
     * @return ImmutableDecimal
380
     * @throws IntegrityConstraint
381
     */
382 33
    public static function makeZero($precision = null)
383
    {
384 33
        return self::make(self::IMMUTABLE, 0, $precision);
385
    }
386
387
}