Completed
Push — dev ( 75154f...13d659 )
by Jordan
13:55
created

Normal   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Test Coverage

Coverage 41.67%

Importance

Changes 0
Metric Value
eloc 67
c 0
b 0
f 0
dl 0
loc 237
ccs 30
cts 72
cp 0.4167
rs 10
wmc 20

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A zScoreOfX() 0 9 1
A random() 0 15 1
A makeFromMean() 0 12 1
A xFromZScore() 0 8 1
A getMean() 0 3 1
A getSD() 0 3 1
A cdf() 0 14 1
A percentBelowX() 0 3 1
A rangeRandom() 0 17 6
A pdf() 0 19 3
A percentAboveX() 0 5 1
A makeFromSd() 0 12 1
1
<?php
2
3
namespace Samsara\Fermat\Provider\Distribution;
4
5
use RandomLib\Factory;
6
use Samsara\Exceptions\UsageError\IntegrityConstraint;
7
use Samsara\Exceptions\UsageError\OptionalExit;
8
use Samsara\Fermat\Numbers;
9
use Samsara\Fermat\Provider\Distribution\Base\Distribution;
10
use Samsara\Fermat\Provider\StatsProvider;
11
use Samsara\Fermat\Types\Base\DecimalInterface;
12
use Samsara\Fermat\Types\Base\NumberCollectionInterface;
13
use Samsara\Fermat\Types\Base\NumberInterface;
14
use Samsara\Fermat\Types\NumberCollection;
15
use Samsara\Fermat\Values\ImmutableNumber;
16
17
class Normal extends Distribution
18
{
19
20
    /**
21
     * @var ImmutableNumber
22
     */
23
    private $mean;
24
25
    /**
26
     * @var ImmutableNumber
27
     */
28
    private $sd;
29
30
    /**
31
     * Normal constructor.
32
     *
33
     * @param int|float|DecimalInterface $mean
34
     * @param int|float|DecimalInterface $sd
35
     * @throws IntegrityConstraint
36
     */
37 4
    public function __construct($mean, $sd)
38
    {
39 4
        $mean = Numbers::makeOrDont(Numbers::IMMUTABLE, $mean);
40 4
        $sd = Numbers::makeOrDont(Numbers::IMMUTABLE, $sd);
41
42 4
        $this->mean = $mean;
0 ignored issues
show
Documentation Bug introduced by
It seems like $mean can also be of type Samsara\Fermat\Values\MutableNumber. However, the property $mean is declared as type Samsara\Fermat\Values\ImmutableNumber. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
43 4
        $this->sd = $sd;
0 ignored issues
show
Documentation Bug introduced by
It seems like $sd can also be of type Samsara\Fermat\Values\MutableNumber. However, the property $sd is declared as type Samsara\Fermat\Values\ImmutableNumber. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
44 4
    }
45
46
    public function getSD()
47
    {
48
        return $this->sd;
49
    }
50
51
    public function getMean()
52
    {
53
        return $this->mean;
54
    }
55
56
    /**
57
     * @param int|float|DecimalInterface $p
58
     * @param int|float|DecimalInterface $x
59
     * @param int|float|DecimalInterface $mean
60
     *
61
     * @return Normal
62
     * @throws IntegrityConstraint
63
     */
64
    public static function makeFromMean($p, $x, $mean): Normal
65
    {
66
        $one = Numbers::makeOne();
67
        $p = Numbers::makeOrDont(Numbers::IMMUTABLE, $p);
68
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
69
        $mean = Numbers::makeOrDont(Numbers::IMMUTABLE, $mean);
70
71
        $z = StatsProvider::inverseNormalCDF($one->subtract($p));
72
73
        $sd = $x->subtract($mean)->divide($z);
0 ignored issues
show
Bug introduced by
The method divide() 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

73
        $sd = $x->subtract($mean)->/** @scrutinizer ignore-call */ divide($z);
Loading history...
Bug introduced by
The method divide() 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

73
        $sd = $x->subtract($mean)->/** @scrutinizer ignore-call */ divide($z);
Loading history...
74
75
        return new Normal($mean, $sd);
0 ignored issues
show
Bug introduced by
It seems like $mean can also be of type Samsara\Fermat\Types\Base\NumberInterface; however, parameter $mean of Samsara\Fermat\Provider\...n\Normal::__construct() does only seem to accept Samsara\Fermat\Types\Bas...nterface|double|integer, maybe add an additional type check? ( Ignorable by Annotation )

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

75
        return new Normal(/** @scrutinizer ignore-type */ $mean, $sd);
Loading history...
Bug introduced by
It seems like $sd can also be of type Samsara\Fermat\Types\Base\FractionInterface and Samsara\Fermat\Types\Base\NumberInterface; however, parameter $sd of Samsara\Fermat\Provider\...n\Normal::__construct() does only seem to accept Samsara\Fermat\Types\Bas...nterface|double|integer, maybe add an additional type check? ( Ignorable by Annotation )

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

75
        return new Normal($mean, /** @scrutinizer ignore-type */ $sd);
Loading history...
76
    }
77
78
    /**
79
     * @param int|float|DecimalInterface $p
80
     * @param int|float|DecimalInterface $x
81
     * @param int|float|DecimalInterface $sd
82
     *
83
     * @return Normal
84
     * @throws IntegrityConstraint
85
     */
86
    public static function makeFromSd($p, $x, $sd): Normal
87
    {
88
        $one = Numbers::makeOne();
89
        $p = Numbers::makeOrDont(Numbers::IMMUTABLE, $p);
90
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
91
        $sd = Numbers::makeOrDont(Numbers::IMMUTABLE, $sd);
92
93
        $z = StatsProvider::inverseNormalCDF($one->subtract($p));
94
95
        $mean = $x->subtract($z->multiply($sd));
0 ignored issues
show
Bug introduced by
The method multiply() 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

95
        $mean = $x->subtract($z->/** @scrutinizer ignore-call */ multiply($sd));
Loading history...
96 3
97
        return new Normal($mean, $sd);
0 ignored issues
show
Bug introduced by
It seems like $mean can also be of type Samsara\Fermat\Types\Base\FractionInterface and Samsara\Fermat\Types\Base\NumberInterface; however, parameter $mean of Samsara\Fermat\Provider\...n\Normal::__construct() does only seem to accept Samsara\Fermat\Types\Bas...nterface|double|integer, maybe add an additional type check? ( Ignorable by Annotation )

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

97
        return new Normal(/** @scrutinizer ignore-type */ $mean, $sd);
Loading history...
Bug introduced by
It seems like $sd can also be of type Samsara\Fermat\Types\Base\NumberInterface; however, parameter $sd of Samsara\Fermat\Provider\...n\Normal::__construct() does only seem to accept Samsara\Fermat\Types\Bas...nterface|double|integer, maybe add an additional type check? ( Ignorable by Annotation )

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

97
        return new Normal($mean, /** @scrutinizer ignore-type */ $sd);
Loading history...
98 3
    }
99
100 3
    /**
101 3
     * @param int|float|DecimalInterface $x
102 3
     *
103
     * @return ImmutableNumber
104
     * @throws IntegrityConstraint
105 3
     */
106 3
    public function cdf($x): ImmutableNumber
107
    {
108
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
109 3
110
        $oneHalf = Numbers::make(Numbers::IMMUTABLE, '0.5');
111
        $one = Numbers::makeOne();
112
        $sqrtTwo = Numbers::make(Numbers::IMMUTABLE, 2)->sqrt();
113
114
        /** @var ImmutableNumber $cdf */
115
        $cdf = $oneHalf->multiply($one->add(StatsProvider::gaussErrorFunction(
0 ignored issues
show
Bug introduced by
The method multiply() 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

115
        /** @scrutinizer ignore-call */ $cdf = $oneHalf->multiply($one->add(StatsProvider::gaussErrorFunction(
Loading history...
Bug introduced by
The method multiply() 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

115
        /** @scrutinizer ignore-call */ $cdf = $oneHalf->multiply($one->add(StatsProvider::gaussErrorFunction(

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 multiply() 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

115
        /** @scrutinizer ignore-call */ $cdf = $oneHalf->multiply($one->add(StatsProvider::gaussErrorFunction(

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...
116
            $x->subtract($this->mean)->divide($this->sd->multiply($sqrtTwo))
117
        )));
118
119 1
        return $cdf;
120
    }
121
122 1
    /**
123
     * @param int|float|DecimalInterface $x1
124 1
     * @param null|int|float|DecimalInterface $x2
125
     *
126
     * @return ImmutableNumber
127
     * @throws IntegrityConstraint
128
     */
129
    public function pdf($x1, $x2 = null): ImmutableNumber
130
    {
131
132
        $x1 = Numbers::makeOrDont(Numbers::IMMUTABLE, $x1);
133
134
        if (is_null($x2)) {
135 1
            $separation = $x1->subtract($this->mean)->multiply(2)->abs();
0 ignored issues
show
Bug introduced by
The method abs() 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

135
            $separation = $x1->subtract($this->mean)->multiply(2)->/** @scrutinizer ignore-call */ abs();
Loading history...
Bug introduced by
The method abs() 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

135
            $separation = $x1->subtract($this->mean)->multiply(2)->/** @scrutinizer ignore-call */ abs();
Loading history...
136
137 1
            if ($this->mean->isLessThan($x1)) {
138
                $x2 = $x1->subtract($separation);
139
            } else {
140
                $x2 = $x1->add($separation);
141
            }
142
        }
143
144
        /** @var ImmutableNumber $pdf */
145
        $pdf = $this->cdf($x1)->subtract($this->cdf($x2))->abs();
0 ignored issues
show
Bug introduced by
It seems like $x1 can also be of type Samsara\Fermat\Types\Base\NumberInterface; however, parameter $x of Samsara\Fermat\Provider\Distribution\Normal::cdf() does only seem to accept Samsara\Fermat\Types\Bas...nterface|double|integer, maybe add an additional type check? ( Ignorable by Annotation )

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

145
        $pdf = $this->cdf(/** @scrutinizer ignore-type */ $x1)->subtract($this->cdf($x2))->abs();
Loading history...
146
147
        return $pdf;
148
    }
149
150
    /**
151
     * @param int|float|DecimalInterface $x
152
     *
153
     * @return ImmutableNumber
154
     * @throws IntegrityConstraint
155
     */
156
    public function percentBelowX($x): ImmutableNumber
157 2
    {
158
        return $this->cdf($x);
159 2
    }
160
161 2
    /**
162
     * @param int|float|DecimalInterface $x
163
     *
164
     * @return ImmutableNumber
165
     * @throws IntegrityConstraint
166
     */
167
    public function percentAboveX($x): ImmutableNumber
168
    {
169
        $one = Numbers::makeOne();
170 1
171
        return $one->subtract($this->cdf($x));
172
    }
173 1
174
    /**
175
     * @param int|float|DecimalInterface $x
176 1
     *
177
     * @return ImmutableNumber
178 1
     * @throws IntegrityConstraint
179
     */
180
    public function zScoreOfX($x): ImmutableNumber
181
    {
182
        /** @var ImmutableNumber $x */
183
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
184
185
        /** @var ImmutableNumber $z */
186
        $z = $x->subtract($this->mean)->divide($this->sd);
187 1
188
        return $z;
189 1
    }
190
191
    /**
192 1
     * @param int|float|DecimalInterface $z
193
     *
194 1
     * @return ImmutableNumber
195
     * @throws IntegrityConstraint
196
     */
197
    public function xFromZScore($z): ImmutableNumber
198
    {
199
        $z = Numbers::makeOrDont(Numbers::IMMUTABLE, $z);
200
201
        /** @var ImmutableNumber $x */
202
        $x = $z->multiply($this->sd)->add($this->mean);
0 ignored issues
show
Bug introduced by
The method add() 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

202
        $x = $z->multiply($this->sd)->/** @scrutinizer ignore-call */ add($this->mean);
Loading history...
Bug introduced by
The method add() 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

202
        $x = $z->multiply($this->sd)->/** @scrutinizer ignore-call */ add($this->mean);
Loading history...
203
204
        return $x;
205
    }
206
207
    /**
208
     * @return ImmutableNumber
209
     * @throws IntegrityConstraint
210
     */
211
    public function random(): ImmutableNumber
212
    {
213
        $randFactory = new Factory();
214
        $generator = $randFactory->getMediumStrengthGenerator();
215
216
        $rand1 = Numbers::make(Numbers::IMMUTABLE, $generator->generateInt(), 20);
217
        $rand1 = $rand1->divide(PHP_INT_MAX);
0 ignored issues
show
Bug introduced by
The method divide() 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

217
        /** @scrutinizer ignore-call */ $rand1 = $rand1->divide(PHP_INT_MAX);

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 divide() 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

217
        /** @scrutinizer ignore-call */ $rand1 = $rand1->divide(PHP_INT_MAX);

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...
218
        $rand2 = Numbers::make(Numbers::IMMUTABLE, $generator->generateInt(), 20);
219
        $rand2 = $rand2->divide(PHP_INT_MAX);
220
221
        $randomNumber = $rand1->ln()->multiply(-2)->sqrt()->multiply($rand2->multiply(Numbers::TAU)->cos(1, false));
0 ignored issues
show
Bug introduced by
The method ln() 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

221
        $randomNumber = $rand1->/** @scrutinizer ignore-call */ ln()->multiply(-2)->sqrt()->multiply($rand2->multiply(Numbers::TAU)->cos(1, false));

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 sqrt() 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

221
        $randomNumber = $rand1->ln()->multiply(-2)->/** @scrutinizer ignore-call */ sqrt()->multiply($rand2->multiply(Numbers::TAU)->cos(1, false));
Loading history...
Bug introduced by
The method sqrt() 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

221
        $randomNumber = $rand1->ln()->multiply(-2)->/** @scrutinizer ignore-call */ sqrt()->multiply($rand2->multiply(Numbers::TAU)->cos(1, false));
Loading history...
Bug introduced by
The method ln() 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

221
        $randomNumber = $rand1->/** @scrutinizer ignore-call */ ln()->multiply(-2)->sqrt()->multiply($rand2->multiply(Numbers::TAU)->cos(1, false));
Loading history...
Bug introduced by
The call to Samsara\Fermat\Types\Base\NumberInterface::sqrt() has too few arguments starting with precision. ( Ignorable by Annotation )

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

221
        $randomNumber = $rand1->ln()->multiply(-2)->/** @scrutinizer ignore-call */ sqrt()->multiply($rand2->multiply(Numbers::TAU)->cos(1, false));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
222
        /** @var ImmutableNumber $randomNumber */
223
        $randomNumber = $randomNumber->multiply($this->sd)->add($this->mean);
224
225
        return $randomNumber;
226
    }
227
228
    /**
229
     * @param int|float|NumberInterface $min
230
     * @param int|float|NumberInterface $max
231
     * @param int $maxIterations
232
     *
233
     * @return ImmutableNumber
234
     * @throws OptionalExit
235
     * @throws IntegrityConstraint
236
     */
237
    public function rangeRandom($min = 0, $max = PHP_INT_MAX, int $maxIterations = 20): ImmutableNumber
238
    {
239
        $i = 0;
240
241
        do {
242
            $randomNumber = $this->random();
243
            $i++;
244
        } while (($randomNumber->isGreaterThan($max) || $randomNumber->isLessThan($min)) && $i < $maxIterations);
245
246
        if ($randomNumber->isGreaterThan($max) || $randomNumber->isLessThan($min)) {
247
            throw new OptionalExit(
248
                'All random numbers generated were outside of the requested range',
249
                'A suitable random number, restricted by the $max ('.$max.') and $min ('.$min.'), could not be found within '.$maxIterations.' iterations'
0 ignored issues
show
Bug introduced by
Are you sure $min of type Samsara\Fermat\Types\Bas...nterface|double|integer can be used in concatenation? ( Ignorable by Annotation )

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

249
                'A suitable random number, restricted by the $max ('.$max.') and $min ('./** @scrutinizer ignore-type */ $min.'), could not be found within '.$maxIterations.' iterations'
Loading history...
Bug introduced by
Are you sure $max of type Samsara\Fermat\Types\Bas...nterface|double|integer can be used in concatenation? ( Ignorable by Annotation )

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

249
                'A suitable random number, restricted by the $max ('./** @scrutinizer ignore-type */ $max.') and $min ('.$min.'), could not be found within '.$maxIterations.' iterations'
Loading history...
250
            );
251
        }
252
253
        return $randomNumber;
254
    }
255
256
}