Completed
Push — master ( ebd7bb...65be67 )
by Tony Karavasilev (Тони
04:40
created

FloatOutputTrait   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 176
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 34
eloc 58
dl 0
loc 176
c 0
b 0
f 0
ccs 62
cts 62
cp 1
rs 9.68

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getProbability() 0 15 6
B getFloat() 0 39 11
B getPercent() 0 24 8
A calculateEpsilon() 0 12 3
A validateDoubleRange() 0 23 6
1
<?php
2
3
/**
4
 * Trait implementation of float format generation for generator services.
5
 */
6
7
namespace CryptoManana\Core\Traits\Randomness;
8
9
/**
10
 * Trait FloatOutputTrait - Reusable implementation of `FloatOutputInterface`.
11
 *
12
 * @see \CryptoManana\Core\Interfaces\Randomness\FloatOutputInterface The abstract specification.
13
 *
14
 * @package CryptoManana\Core\Traits\Randomness
15
 */
16
trait FloatOutputTrait
17
{
18
    /**
19
     * Internal method for calculating the machine epsilon value based on the used precision.
20
     *
21
     * Note: Passing `null` will use the global system precision value.
22
     *
23
     * @param null|int $precision The wanted precision for the machine epsilon.
24
     *
25
     * @return float The machine epsilon used for floating number comparison operations.
26
     */
27 6
    protected function calculateEpsilon($precision = null)
28
    {
29 6
        $precision = ($precision === null) ? self::$systemPrecision : $precision;
30
31
        // Calculate epsilon based on precision digits
32 6
        $epsilon = 0.1;
33
34 6
        for ($i = 1; $i < $precision; $i++) {
35 6
            $epsilon *= 0.1;
36
        }
37
38 6
        return $epsilon;
39
    }
40
41
    /**
42
     * Internal method for double range validation.
43
     *
44
     * @param int|float $from The minimum number in the wanted range.
45
     * @param int|float $to The maximum number in the wanted range.
46
     * @param null|int $precision The used precision for comparison.
47
     *
48
     * @throws \Exception Validation errors.
49
     */
50 15
    protected function validateDoubleRange($from, $to, $precision = 14)
51
    {
52 15
        $precision = ($precision === null) ? self::$systemPrecision : $precision;
53
54 15
        if ($from < (float)$this->getMinNumber() || $to > (float)$this->getMaxNumber()) {
0 ignored issues
show
Bug introduced by
It seems like getMinNumber() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

54
        if ($from < (float)$this->/** @scrutinizer ignore-call */ getMinNumber() || $to > (float)$this->getMaxNumber()) {
Loading history...
Bug introduced by
It seems like getMaxNumber() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

54
        if ($from < (float)$this->getMinNumber() || $to > (float)$this->/** @scrutinizer ignore-call */ getMaxNumber()) {
Loading history...
55 6
            throw new \DomainException(
56 6
                "The provided values are out of the supported range."
57
            );
58
        }
59
60 9
        if ($from > $to) {
61 3
            throw new \LogicException(
62 3
                "The chosen generation maximum is less or equal the provided minimum value."
63
            );
64
        }
65
66 6
        $epsilon = $this->calculateEpsilon($precision);
67
68 6
        $difference = abs($from - $to);
69
70 6
        if ($difference < $epsilon) {
71 3
            throw new \LogicException(
72 3
                "The chosen generation maximum is less or equal the provided minimum value."
73
            );
74
        }
75 3
    }
76
77
    /**
78
     * Generate a probability format float number between 0.0 and 1.0.
79
     *
80
     * Note: Passing `null` will use the global system precision value.
81
     *
82
     * @param null|int $precision Rounding precision (default => 10).
83
     *
84
     * @return float Randomly generated probability value.
85
     * @throws \Exception Validation errors.
86
     */
87 3
    public function getProbability($precision = 10)
88
    {
89 3
        $precision = ($precision === null) ? self::$systemPrecision : $precision;
90
91 3
        $this->validatePositiveInteger($precision, true);
0 ignored issues
show
Bug introduced by
It seems like validatePositiveInteger() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

91
        $this->/** @scrutinizer ignore-call */ 
92
               validatePositiveInteger($precision, true);
Loading history...
92
93 3
        $number = $this->getInt(0, $this->getMaxNumber());
0 ignored issues
show
Bug introduced by
It seems like getInt() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

93
        /** @scrutinizer ignore-call */ 
94
        $number = $this->getInt(0, $this->getMaxNumber());
Loading history...
94
95 3
        $isNotRangeBoarders = ($number !== 0 && $number !== $this->getMaxNumber());
96
97 3
        $number = ($number === 0) ? 0.00 : ($number === $this->getMaxNumber()) ? 100.00 : (float)$number;
98
99 3
        $number = $isNotRangeBoarders ? round($number / (float)$this->getMaxNumber(), $precision) : $number;
100
101 3
        return $number;
102
    }
103
104
    /**
105
     * Generate a random float number in a certain range.
106
     *
107
     * Note: Passing `null` will use the default parameter value or for precision the global system value.
108
     *
109
     * @param null|float|int $from The lowest value to be returned (default => 0.0).
110
     * @param null|float|int $to The highest value to be returned (default => (float)$this->getMaxNumber()).
111
     * @param null|int $precision Rounding precision (default => 8).
112
     *
113
     * @return float Randomly generated float value.
114
     * @throws \Exception Validation errors.
115
     */
116 18
    public function getFloat($from = 0.0, $to = null, $precision = 8)
117
    {
118 18
        $precision = ($precision === null) ? self::$systemPrecision : $precision;
119
120 18
        $this->validatePositiveInteger($precision, true);
121
122 18
        $fromInvalidType = !in_array(gettype($from), ['integer', 'double', 'NULL']);
123 18
        $toInvalidType = !in_array(gettype($to), ['integer', 'double', 'NULL']);
124
125 18
        if ($fromInvalidType || $toInvalidType) {
126 3
            throw new \DomainException(
127 3
                "The provided values are of invalid type."
128
            );
129
        }
130
131 15
        $from = ($from === null) ? 0.0 : (float)$from;
132 15
        $to = ($to === null) ? (float)$this->getMaxNumber() : (float)$to;
133
134 15
        $this->validateDoubleRange($from, $to, $precision);
135
136 3
        $epsilon = $this->calculateEpsilon($precision);
137
138 3
        $toIsTheMaximum = abs($this->getMaxNumber() - $to) < $epsilon;
139 3
        $fromIsTheZero = abs(0.0 - $from) < $epsilon;
140
141 3
        $fromIsTheMinimumPlusOne = abs(($this->getMinNumber() + 1.0) - $from) < $epsilon;
142 3
        $toIsTheZero = abs(0.0 - $to) < $epsilon;
143
144
        // Improves the overall calculation quality for range calls
145 3
        if ($toIsTheMaximum && $fromIsTheZero) {
146 3
            $from = 0.01;
147 3
        } elseif ($toIsTheZero && $fromIsTheMinimumPlusOne) {
148 3
            $to = 0.01;
149
        }
150
151
        // Minimum precision for probability fetching
152 3
        $scope = ($precision > 14) ? $precision : 14;
153
154 3
        return round($from + $this->getProbability($scope) * abs($to - $from), $precision);
155
    }
156
157
    /**
158
     * Generate a percentage format float number between 0.0 and 100.0.
159
     *
160
     * Note: Passing `null` will use the global system precision value.
161
     *
162
     * @param null|int $precision Rounding precision (default => 2).
163
     * @param bool|int $lowerTheScope Flag for using a smaller calculation range.
164
     *
165
     * @return float Randomly generated percentage value.
166
     * @throws \Exception Validation errors.
167
     */
168 3
    public function getPercent($precision = 2, $lowerTheScope = false)
169
    {
170 3
        $precision = ($precision === null) ? self::$systemPrecision : $precision;
171
172 3
        $this->validatePositiveInteger($precision, true);
173
174 3
        if ($lowerTheScope) {
175 3
            $max = 9999; // 0-9999
176
177 3
            $number = $this->getInt(0, $max);
178
179 3
            $isNotRangeBoarders = ($number !== 0 && $number !== $max);
180
181 3
            $number = ($number === 0) ? 0.00 : ($number === $max) ? 100.00 : $number;
182
183 3
            $number = $isNotRangeBoarders ? ($number / $max) * 100.00 : $number;
184
        } else {
185
            // Minimum precision for probability fetching
186 3
            $scope = ($precision > 14) ? $precision : 14;
187
188 3
            $number = $this->getProbability($scope) * 100.00;
189
        }
190
191 3
        return round($number, $precision);
192
    }
193
}
194