Failed Conditions
Pull Request — dev (#50)
by Jordan
09:06 queued 04:26
created

Numbers::makeGoldenRatio()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

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

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

136
        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

136
        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...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

136
        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...
137
    }
138
139
    /**
140
     * @param string|object $type
141
     * @param int|float|string|array|NumberInterface|DecimalInterface|FractionInterface $value
142
     * @param int|null $precision
143
     * @param int $base
144
     *
145
     * @return ImmutableDecimal|MutableDecimal|NumberInterface|ImmutableDecimal[]|MutableDecimal[]|NumberInterface[]
146
     * @throws IntegrityConstraint|ReflectionException
147
     */
148 108
    public static function makeOrDont($type, $value, $precision = null, $base = 10)
149
    {
150
151 108
        if (is_object($value)) {
152 84
            if ($value instanceof $type) {
153 84
                return $value;
154
            }
155
156 4
            if ($value instanceof NumberInterface) {
0 ignored issues
show
introduced by
$value is always a sub-type of Samsara\Fermat\Types\Bas...Numbers\NumberInterface.
Loading history...
157 4
                return static::make($type, $value->getValue(), $precision, $base);
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on Samsara\Fermat\Types\Bas...Numbers\NumberInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Samsara\Fermat\Types\Bas...Numbers\NumberInterface. ( Ignorable by Annotation )

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

157
                return static::make($type, $value->/** @scrutinizer ignore-call */ getValue(), $precision, $base);
Loading history...
158
            }
159 101
        } elseif (is_array($value)) {
160 13
            $newInput = [];
161
162 13
            foreach ($value as $key => $item) {
163 13
                $newInput[$key] = static::makeOrDont($type, $item, $precision, $base);
164
            }
165
166 13
            return $newInput;
167 101
        } 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...
168 101
            $isImaginary = strpos($value, 'i') !== false;
169
170 101
            if (is_numeric($value) || $isImaginary) {
171 101
                return static::make($type, $value, $precision, $base);
172
            }
173
        }
174
175
        throw new IntegrityConstraint(
176
            '$input must be an int, float, numeric string, or an implementation of NumberInterface',
177
            'Provide any of the MANY valid inputs',
178
            'The $input argument was not numeric or an implementation of NumberInterface. Given value: '.$value
179
        );
180
181
    }
182
183
    /**
184
     * @param     $type
185
     * @param     $value
186
     * @param int $base
187
     *
188
     * @return FractionInterface|ImmutableFraction|MutableFraction
189
     * @throws IntegrityConstraint|ReflectionException
190
     */
191 11
    public static function makeFractionFromString($type, $value, int $base = 10): FractionInterface
192
    {
193 11
        $parts = explode('/', $value);
194
195 11
        if (count($parts) > 2) {
196
            throw new IntegrityConstraint(
197
                'Only one division symbol (/) can be used',
198
                'Change the calling code to not provide more than one division symbol',
199
                'makeFractionFromString needs either one or zero division symbols in the $value argument; '.$value.' given'
200
            );
201
        }
202
203
        /** @var ImmutableDecimal $numerator */
204 11
        $numerator = self::make(self::IMMUTABLE, trim(ltrim($parts[0])))->round();
205
        /** @var ImmutableDecimal $denominator */
206 11
        $denominator = isset($parts[1]) ? self::make(self::IMMUTABLE, trim(ltrim($parts[1])))->round() : self::makeOne();
207
208 11
        if ($type === self::IMMUTABLE_FRACTION) {
209 11
            return new ImmutableFraction($numerator, $denominator, $base);
210
        }
211
212 3
        if ($type === self::MUTABLE_FRACTION) {
213 3
            return new MutableFraction($numerator, $denominator, $base);
214
        }
215
216
        $reflector = new \ReflectionClass($type);
217
218
        if ($reflector->implementsInterface(FractionInterface::class) && $reflector->isSubclassOf(Fraction::class)) {
219
            /** @var FractionInterface|Fraction $customFraction */
220
            $customFraction = $reflector->newInstance([
221
                $numerator,
222
                $denominator,
223
                $base
224
            ]);
225
            return $customFraction;
226
        }
227
228
        throw new IntegrityConstraint(
229
            'Type must be an implementation of FractionInterface',
230
            'Alter to calling code to use the correct type',
231
            'makeFractionFromString can only make objects which implement the FractionInterface; '.$type.' given'
232
        );
233
    }
234
235
    /**
236
     * @param int|null $precision
237
     *
238
     * @return NumberInterface
239
     * @throws ReflectionException
240
     * @throws IntegrityConstraint
241
     */
242 33
    public static function makePi(int $precision = null)
243
    {
244
245 33
        if (!is_null($precision)) {
246 25
            if ($precision > 100 || $precision < 1) {
247 1
                throw new IntegrityConstraint(
248 1
                    '$precision must be between 1 and 100 inclusive',
249 1
                    'Provide a precision within range',
250 1
                    'The PI constant cannot have a precision higher than the constant stored (100)'
251
                );
252
            }
253
254 25
            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...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

254
            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...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

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