Completed
Pull Request — dev (#33)
by Jordan
01:55
created

Numbers::makeFromBase10()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 4
dl 0
loc 8
ccs 3
cts 3
cp 1
crap 1
rs 10
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 58
    public static function make($type, $value, $precision = null, $base = 10)
46
    {
47
48 58
        if (is_object($type)) {
49 29
            $type = get_class($type);
50
        }
51
52 58
        if ($type == static::IMMUTABLE) {
53 58
            return new ImmutableNumber(trim($value), $precision, $base);
54 6
        } elseif ($type == static::MUTABLE) {
55 5
            return new MutableNumber(trim($value), $precision, $base);
56 4
        } elseif ($type == static::IMMUTABLE_FRACTION) {
57 3
            return self::makeFractionFromString($type, $value, $base);
58 4
        } elseif ($type == static::MUTABLE_FRACTION) {
59 3
            return self::makeFractionFromString($type, $value, $base);
60 1
        } elseif ($type == static::CARTESIAN_COORDINATE && is_array($value)) {
61 1
            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 1
    public static function makeFromBase10($type, $value, $precision = null, $base = 10)
111
    {
112
        /**
113
         * @var ImmutableNumber|MutableNumber
114
         */
115 1
        $number = self::make($type, $value, $precision, 10);
116
117 1
        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 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...
Bug Best Practice introduced by
The expression return $number->convertToBase($base) also could return the type Samsara\Fermat\Types\Bas...a\Fermat\Types\Fraction which is incompatible with the documented return type Samsara\Fermat\Types\Base\NumberInterface.
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 55
    public static function makeOrDont($type, $value, $precision = null, $base = 10)
130
    {
131
132 55
        if (is_object($value)) {
133 47
            $reflector = new \ReflectionClass($value);
134
135 47
            if ($value instanceof $type) {
136 47
                return $value;
137
            }
138
139 2
            if ($reflector->implementsInterface(NumberInterface::class)) {
140 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\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 47
        } elseif (is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
143 4
            $newInput = [];
144
145 4
            foreach ($value as $key => $item) {
146 4
                $newInput[$key] = static::makeOrDont($type, $item, $precision, $base);
147
            }
148
149 4
            return $newInput;
150 47
        } elseif (is_numeric($value)) {
151 47
            return static::make($type, $value, $precision, $base);
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 3
    public static function makeFractionFromString($type, $value, $base = 10)
171
    {
172 3
        $parts = explode('/', $value);
173
174 3
        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 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\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 3
        $denominator = isset($parts[1]) ? Numbers::make(Numbers::IMMUTABLE, trim(ltrim($parts[1])))->round() : Numbers::makeOne();
186
187 3
        if ($type == self::IMMUTABLE_FRACTION) {
188 3
            return new ImmutableFraction($numerator, $denominator, $base);
189 3
        } elseif ($type == self::MUTABLE_FRACTION) {
190 3
            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 11
    public static function makePi($precision = null)
219
    {
220
        
221 11
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
222 1
            throw new IntegrityConstraint(
223 1
                '$precision must be between 1 and 100 inclusive',
224 1
                'Provide a precision within range',
225 1
                'The PI constant cannot have a precision higher than the constant stored (100)'
226
            );
227
        }
228
        
229 11
        if (!is_null($precision)) {
230 1
            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\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 */ 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\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 */ truncateToPrecision($precision);
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

230
            return self::make(self::IMMUTABLE, self::PI, $precision)->/** @scrutinizer ignore-call */ truncateToPrecision($precision);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method truncateToPrecision() does not exist on Samsara\Fermat\Values\MutableFraction. ( Ignorable by Annotation )

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

230
            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...
231
        } else {
232 11
            return self::make(self::IMMUTABLE, self::PI, 100);
233
        }
234
        
235
    }
236
237
    /**
238
     * @param int|null $precision
239
     *
240
     * @throws IntegrityConstraint
241
     * @return NumberInterface
242
     */
243 7
    public static function makeTau($precision = null)
244
    {
245 7
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
246 1
            throw new IntegrityConstraint(
247 1
                '$precision must be between 1 and 100 inclusive',
248 1
                'Provide a precision within range',
249 1
                'The TAU constant cannot have a precision higher than the constant stored (100)'
250
            );
251
        }
252
253 7
        if (!is_null($precision)) {
254 1
            return self::make(self::IMMUTABLE, self::TAU, $precision)->truncateToPrecision($precision);
255
        } else {
256 7
            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 7
    public static function make2Pi($precision = null)
266
    {
267 7
        return self::makeTau($precision);
268
    }
269
270
    /**
271
     * @param int|null $precision
272
     *
273
     * @throws IntegrityConstraint
274
     * @return NumberInterface
275
     */
276 6
    public static function makeE($precision = null)
277
    {
278
279 6
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
280 1
            throw new IntegrityConstraint(
281 1
                '$precision must be between 1 and 100 inclusive',
282 1
                'Provide a precision within range',
283 1
                'The E constant cannot have a precision higher than the constant stored (100)'
284
            );
285
        }
286
287 6
        if (!is_null($precision)) {
288 1
            return self::make(self::IMMUTABLE, self::E, $precision)->truncateToPrecision($precision);
289
        } else {
290 6
            return self::make(self::IMMUTABLE, self::E, 100);
291
        }
292
293
    }
294
295
    /**
296
     * @param int|null $precision
297
     *
298
     * @throws IntegrityConstraint
299
     * @return NumberInterface
300
     */
301 1
    public static function makeGoldenRatio($precision = null)
302
    {
303
304 1
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
305 1
            throw new IntegrityConstraint(
306 1
                '$precision must be between 1 and 100 inclusive',
307 1
                'Provide a precision within range',
308 1
                'The Golden Ratio constant cannot have a precision higher than the constant stored (100)'
309
            );
310
        }
311
312 1
        if (!is_null($precision)) {
313 1
            return self::make(self::IMMUTABLE, self::GOLDEN_RATIO, $precision)->truncateToPrecision($precision);
314
        } else {
315 1
            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 2
    public static function makeNaturalLog10($precision = null)
327
    {
328
329 2
        if (!is_null($precision) && ($precision > 100 || $precision < 1)) {
330 1
            throw new IntegrityConstraint(
331 1
                '$precision must be between 1 and 100 inclusive',
332 1
                'Provide a precision within range',
333 1
                'The natural log of 10 constant cannot have a precision higher than the constant stored (100)'
334
            );
335
        }
336
337 2
        if (!is_null($precision)) {
338 1
            return self::make(self::IMMUTABLE, self::LN_10, $precision)->truncateToPrecision($precision);
339
        } else {
340 2
            return self::make(self::IMMUTABLE, self::LN_10, 100);
341
        }
342
343
    }
344
345
    /**
346
     * @param int|null $precision
347
     *
348
     * @return ImmutableNumber
349
     */
350 19
    public static function makeOne($precision = null)
351
    {
352 19
        return self::make(self::IMMUTABLE, 1, $precision);
353
    }
354
355
    /**
356
     * @param int|null $precision
357
     *
358
     * @return ImmutableNumber
359
     */
360 18
    public static function makeZero($precision = null)
361
    {
362 18
        return self::make(self::IMMUTABLE, 0, $precision);
363
    }
364
365
}