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

Normal::zScoreOfX()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 9
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
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
    /**
47
     * @param int|float|DecimalInterface $p
48
     * @param int|float|DecimalInterface $x
49
     * @param int|float|DecimalInterface $mean
50
     *
51
     * @return Normal
52
     * @throws IntegrityConstraint
53
     */
54
    public static function makeFromMean($p, $x, $mean): Normal
55
    {
56
        $one = Numbers::makeOne();
57
        $p = Numbers::makeOrDont(Numbers::IMMUTABLE, $p);
58
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
59
        $mean = Numbers::makeOrDont(Numbers::IMMUTABLE, $mean);
60
61
        $z = StatsProvider::inverseNormalCDF($one->subtract($p));
62
63
        $sd = $x->subtract($mean)->divide($z);
0 ignored issues
show
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

63
        $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\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

63
        $sd = $x->subtract($mean)->/** @scrutinizer ignore-call */ divide($z);
Loading history...
64
65
        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

65
        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

65
        return new Normal($mean, /** @scrutinizer ignore-type */ $sd);
Loading history...
66
    }
67
68
    /**
69
     * @param int|float|DecimalInterface $p
70
     * @param int|float|DecimalInterface $x
71
     * @param int|float|DecimalInterface $sd
72
     *
73
     * @return Normal
74
     * @throws IntegrityConstraint
75
     */
76
    public static function makeFromSd($p, $x, $sd): Normal
77
    {
78
        $one = Numbers::makeOne();
79
        $p = Numbers::makeOrDont(Numbers::IMMUTABLE, $p);
80
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
81
        $sd = Numbers::makeOrDont(Numbers::IMMUTABLE, $sd);
82
83
        $z = StatsProvider::inverseNormalCDF($one->subtract($p));
84
85
        $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

85
        $mean = $x->subtract($z->/** @scrutinizer ignore-call */ multiply($sd));
Loading history...
86
87
        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

87
        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

87
        return new Normal($mean, /** @scrutinizer ignore-type */ $sd);
Loading history...
88
    }
89
90
    /**
91
     * @param int|float|DecimalInterface $x
92
     *
93
     * @return ImmutableNumber
94
     * @throws IntegrityConstraint
95
     */
96 3
    public function cdf($x): ImmutableNumber
97
    {
98 3
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
99
100 3
        $oneHalf = Numbers::make(Numbers::IMMUTABLE, '0.5');
101 3
        $one = Numbers::makeOne();
102 3
        $sqrtTwo = Numbers::make(Numbers::IMMUTABLE, 2)->sqrt();
103
104
        /** @var ImmutableNumber $cdf */
105 3
        $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

105
        /** @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

105
        /** @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

105
        /** @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...
106 3
            $x->subtract($this->mean)->divide($this->sd->multiply($sqrtTwo))
107
        )));
108
109 3
        return $cdf;
110
    }
111
112
    /**
113
     * @param int|float|DecimalInterface $x1
114
     * @param null|int|float|DecimalInterface $x2
115
     *
116
     * @return ImmutableNumber
117
     * @throws IntegrityConstraint
118
     */
119 1
    public function pdf($x1, $x2 = null): ImmutableNumber
120
    {
121
122 1
        $x1 = Numbers::makeOrDont(Numbers::IMMUTABLE, $x1);
123
124 1
        if (is_null($x2)) {
125
            $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

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

125
            $separation = $x1->subtract($this->mean)->multiply(2)->/** @scrutinizer ignore-call */ abs();
Loading history...
126
127
            if ($this->mean->isLessThan($x1)) {
128
                $x2 = $x1->subtract($separation);
129
            } else {
130
                $x2 = $x1->add($separation);
131
            }
132
        }
133
134
        /** @var ImmutableNumber $pdf */
135 1
        $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

135
        $pdf = $this->cdf(/** @scrutinizer ignore-type */ $x1)->subtract($this->cdf($x2))->abs();
Loading history...
136
137 1
        return $pdf;
138
    }
139
140
    /**
141
     * @param int|float|DecimalInterface $x
142
     *
143
     * @return ImmutableNumber
144
     * @throws IntegrityConstraint
145
     */
146
    public function percentBelowX($x): ImmutableNumber
147
    {
148
        return $this->cdf($x);
149
    }
150
151
    /**
152
     * @param int|float|DecimalInterface $x
153
     *
154
     * @return ImmutableNumber
155
     * @throws IntegrityConstraint
156
     */
157 2
    public function percentAboveX($x): ImmutableNumber
158
    {
159 2
        $one = Numbers::makeOne();
160
161 2
        return $one->subtract($this->cdf($x));
162
    }
163
164
    /**
165
     * @param int|float|DecimalInterface $x
166
     *
167
     * @return ImmutableNumber
168
     * @throws IntegrityConstraint
169
     */
170 1
    public function zScoreOfX($x): ImmutableNumber
171
    {
172
        /** @var ImmutableNumber $x */
173 1
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
174
175
        /** @var ImmutableNumber $z */
176 1
        $z = $x->subtract($this->mean)->divide($this->sd);
177
178 1
        return $z;
179
    }
180
181
    /**
182
     * @param int|float|DecimalInterface $z
183
     *
184
     * @return ImmutableNumber
185
     * @throws IntegrityConstraint
186
     */
187 1
    public function xFromZScore($z): ImmutableNumber
188
    {
189 1
        $z = Numbers::makeOrDont(Numbers::IMMUTABLE, $z);
190
191
        /** @var ImmutableNumber $x */
192 1
        $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

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

192
        $x = $z->multiply($this->sd)->/** @scrutinizer ignore-call */ add($this->mean);
Loading history...
193
194 1
        return $x;
195
    }
196
197
    /**
198
     * @return ImmutableNumber
199
     * @throws IntegrityConstraint
200
     */
201
    public function random(): ImmutableNumber
202
    {
203
        $randFactory = new Factory();
204
        $generator = $randFactory->getMediumStrengthGenerator();
205
206
        $rand1 = Numbers::make(Numbers::IMMUTABLE, $generator->generateInt(), 20);
207
        $rand1 = $rand1->divide(PHP_INT_MAX);
208
        $rand2 = Numbers::make(Numbers::IMMUTABLE, $generator->generateInt(), 20);
209
        $rand2 = $rand2->divide(PHP_INT_MAX);
210
211
        $randomNumber = $rand1->ln()->multiply(-2)->sqrt()->multiply($rand2->multiply(Numbers::TAU)->cos(1, false));
0 ignored issues
show
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

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

211
        $randomNumber = $rand1->/** @scrutinizer ignore-call */ ln()->multiply(-2)->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\FractionInterface. ( Ignorable by Annotation )

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

211
        $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 cos() 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

211
        $randomNumber = $rand1->ln()->multiply(-2)->sqrt()->multiply($rand2->multiply(Numbers::TAU)->/** @scrutinizer ignore-call */ 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 cos() 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

211
        $randomNumber = $rand1->ln()->multiply(-2)->sqrt()->multiply($rand2->multiply(Numbers::TAU)->/** @scrutinizer ignore-call */ cos(1, false));
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

211
        $randomNumber = $rand1->ln()->multiply(-2)->/** @scrutinizer ignore-call */ 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

211
        $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...
212
        /** @var ImmutableNumber $randomNumber */
213
        $randomNumber = $randomNumber->multiply($this->sd)->add($this->mean);
214
215
        return $randomNumber;
216
    }
217
218
    /**
219
     * @param int|float|NumberInterface $min
220
     * @param int|float|NumberInterface $max
221
     * @param int $maxIterations
222
     *
223
     * @return ImmutableNumber
224
     * @throws OptionalExit
225
     * @throws IntegrityConstraint
226
     */
227
    public function rangeRandom($min = 0, $max = PHP_INT_MAX, int $maxIterations = 20): ImmutableNumber
228
    {
229
        $i = 0;
230
231
        do {
232
            $randomNumber = $this->random();
233
            $i++;
234
        } while (($randomNumber->isGreaterThan($max) || $randomNumber->isLessThan($min)) && $i < $maxIterations);
235
236
        if ($randomNumber->isGreaterThan($max) || $randomNumber->isLessThan($min)) {
237
            throw new OptionalExit(
238
                'All random numbers generated were outside of the requested range',
239
                '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

239
                '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

239
                'A suitable random number, restricted by the $max ('./** @scrutinizer ignore-type */ $max.') and $min ('.$min.'), could not be found within '.$maxIterations.' iterations'
Loading history...
240
            );
241
        }
242
243
        return $randomNumber;
244
    }
245
246
}