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

StatsProvider::inverseNormalCDF()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 16
nc 6
nop 2
dl 0
loc 27
ccs 0
cts 16
cp 0
crap 20
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
namespace Samsara\Fermat\Provider;
4
5
use Samsara\Exceptions\UsageError\IntegrityConstraint;
6
use Samsara\Exceptions\SystemError\LogicalError\IncompatibleObjectState;
7
use Samsara\Fermat\Numbers;
8
use Samsara\Fermat\Types\Base\NumberInterface;
9
use Samsara\Fermat\Types\Base\DecimalInterface;
10
use Samsara\Fermat\Types\Base\FractionInterface;
11
use Samsara\Fermat\Values\ImmutableNumber;
12
13
class StatsProvider
14
{
15
16
    /**
17
     * @param $x
18
     *
19
     * @return NumberInterface
20
     * @throws IntegrityConstraint
21
     */
22
    public static function normalCDF($x)
23
    {
24
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
25
26
        $pi = Numbers::makePi();
27
        $e = Numbers::makeE();
28
        $one = Numbers::makeOne();
29
30
        $eExponent = Numbers::make(Numbers::IMMUTABLE, $x->getValue());
31
        $eExponent = $eExponent->pow(2)->divide(2)->multiply(-1);
32
33
        $answer = Numbers::make(Numbers::IMMUTABLE, 0.5);
34
        $answer = $answer->add(
35
            $one->divide($pi->multiply(2)->sqrt())
0 ignored issues
show
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

35
            $one->divide($pi->multiply(2)->/** @scrutinizer ignore-call */ sqrt())
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

35
            $one->divide($pi->multiply(2)->/** @scrutinizer ignore-call */ sqrt())
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

35
            $one->divide($pi->multiply(2)->/** @scrutinizer ignore-call */ sqrt())

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...
36
                ->multiply($e->pow($eExponent))
37
                ->multiply(SeriesProvider::maclaurinSeries(
38
                    $x,
39
                    function ($n) {
0 ignored issues
show
Unused Code introduced by
The parameter $n is not used and could be removed. ( Ignorable by Annotation )

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

39
                    function (/** @scrutinizer ignore-unused */ $n) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
40
                        return Numbers::makeOne();
41
                    },
42
                    function ($n) {
43
                        return SequenceProvider::nthOddNumber($n);
44
                    },
45
                    function ($n) {
46
                        return SequenceProvider::nthOddNumber($n)->doubleFactorial();
0 ignored issues
show
Bug introduced by
The method doubleFactorial() 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

46
                        return SequenceProvider::nthOddNumber($n)->/** @scrutinizer ignore-call */ doubleFactorial();
Loading history...
47
                    }
48
                ))
49
        );
50
51
        return $answer;
52
53
    }
54
55
    /**
56
     * @param $x
57
     *
58
     * @return DecimalInterface|NumberInterface
59
     * @throws IntegrityConstraint
60
     */
61
    public static function complementNormalCDF($x)
62
    {
63
        $p = self::normalCDF($x);
64
        $one = Numbers::makeOne();
65
66
        return $one->subtract($p);
67
    }
68
69
    /**
70
     * @param $x
71
     *
72
     * @return DecimalInterface|FractionInterface|NumberInterface|ImmutableNumber
73
     * @throws IntegrityConstraint
74
     */
75 3
    public static function gaussErrorFunction($x)
76
    {
77
78 3
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
79 3
        $answer = Numbers::makeOne();
80 3
        $pi = Numbers::makePi();
81
82 3
        $answer = $answer->multiply(2)->divide($pi->sqrt());
0 ignored issues
show
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

82
        $answer = $answer->multiply(2)->divide($pi->/** @scrutinizer ignore-call */ sqrt());

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...
83
84 3
        $answer = $answer->multiply(
85 3
            SeriesProvider::maclaurinSeries(
86 3
                $x,
87 3
                function ($n) {
88 3
                    $negOne = Numbers::make(Numbers::IMMUTABLE, -1);
89
90 3
                    return $negOne->pow($n);
0 ignored issues
show
Bug introduced by
The method pow() 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

90
                    return $negOne->/** @scrutinizer ignore-call */ pow($n);
Loading history...
Bug introduced by
The method pow() 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

90
                    return $negOne->/** @scrutinizer ignore-call */ pow($n);

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

90
                    return $negOne->/** @scrutinizer ignore-call */ pow($n);

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...
91 3
                },
92 3
                function ($n) {
93 3
                    return SequenceProvider::nthOddNumber($n);
94 3
                },
95 3
                function ($n) {
96 3
                    $n = Numbers::makeOrDont(Numbers::IMMUTABLE, $n);
97
98 3
                    return $n->factorial()->multiply(SequenceProvider::nthOddNumber($n));
0 ignored issues
show
Bug introduced by
The method factorial() 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

98
                    return $n->/** @scrutinizer ignore-call */ factorial()->multiply(SequenceProvider::nthOddNumber($n));
Loading history...
99 3
                }
100
            )
101
        );
102
103 3
        return $answer;
104
105
    }
106
107
    /**
108
     * @param     $p
109
     * @param int $precision
110
     *
111
     * @return DecimalInterface|NumberInterface|ImmutableNumber
112
     * @throws IntegrityConstraint
113
     */
114
    public static function inverseNormalCDF($p, int $precision = 10)
115
    {
116
        $pi = Numbers::makePi();
117
        $r2pi = $pi->multiply(2)->sqrt();
0 ignored issues
show
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

117
        $r2pi = $pi->multiply(2)->/** @scrutinizer ignore-call */ sqrt();

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...
118
        $e = Numbers::makeE();
119
        $p = Numbers::makeOrDont(Numbers::IMMUTABLE, $p);
120
121
        $continue = true;
122
123
        $xCur = Numbers::make(Numbers::IMMUTABLE, $p);
124
125
        while ($continue) {
126
127
            $cumulative = self::normalCDF($xCur);
128
            $dx = $cumulative->subtract($p)->divide($r2pi->multiply($e->pow($xCur->pow(2))->divide(-2)));
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

128
            $dx = $cumulative->subtract($p)->divide($r2pi->multiply($e->pow($xCur->pow(2))->/** @scrutinizer ignore-call */ divide(-2)));
Loading history...
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

128
            $dx = $cumulative->subtract($p)->divide($r2pi->/** @scrutinizer ignore-call */ multiply($e->pow($xCur->pow(2))->divide(-2)));
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

128
            $dx = $cumulative->subtract($p)->divide($r2pi->multiply($e->pow($xCur->pow(2))->/** @scrutinizer ignore-call */ divide(-2)));
Loading history...
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

128
            $dx = $cumulative->subtract($p)->divide($r2pi->/** @scrutinizer ignore-call */ multiply($e->pow($xCur->pow(2))->divide(-2)));
Loading history...
129
            $xCur = $xCur->subtract($dx);
130
131
            if ($dx->numberOfLeadingZeros() > $precision) {
0 ignored issues
show
Bug introduced by
The method numberOfLeadingZeros() 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

131
            if ($dx->/** @scrutinizer ignore-call */ numberOfLeadingZeros() > $precision) {
Loading history...
Bug introduced by
The method numberOfLeadingZeros() 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

131
            if ($dx->/** @scrutinizer ignore-call */ numberOfLeadingZeros() > $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...
132
                $continue = false;
133
            }
134
135
        }
136
137
        if ($p->isLessThan(0.5)) {
138
            return $xCur->multiply(-1);
139
        } else {
140
            return $xCur;
141
        }
142
    }
143
144
    /**
145
     * @param $n
146
     * @param $k
147
     *
148
     * @return DecimalInterface|NumberInterface|ImmutableNumber
149
     * @throws IntegrityConstraint
150
     * @throws IncompatibleObjectState
151
     */
152
    public static function binomialCoefficient($n, $k)
153
    {
154
155
        $n = Numbers::makeOrDont(Numbers::IMMUTABLE, $n);
156
        $k = Numbers::makeOrDont(Numbers::IMMUTABLE, $k);
157
158
        if ($k->isLessThan(0) || $n->isLessThan($k)) {
159
            throw new IntegrityConstraint(
160
                '$k must be larger or equal to 0 and less than or equal to $n',
161
                'Provide valid $n and $k values such that 0 <= $k <= $n',
162
                'For $n choose $k, the values of $n and $k must satisfy the inequality 0 <= $k <= $n'
163
            );
164
        }
165
166
        if (!$n->isInt() || !$k->isInt()) {
0 ignored issues
show
Bug introduced by
The method isInt() does not exist on Samsara\Fermat\Types\Base\NumberInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Samsara\Fermat\Types\Base\NumberInterface. ( Ignorable by Annotation )

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

166
        if (!$n->/** @scrutinizer ignore-call */ isInt() || !$k->isInt()) {
Loading history...
167
            throw new IntegrityConstraint(
168
                '$k and $n must be whole numbers',
169
                'Provide whole numbers for $n and $k',
170
                'For $n choose $k, the values $n and $k must be whole numbers'
171
            );
172
        }
173
174
        return $n->factorial()->divide($k->factorial()->multiply($n->subtract($k)->factorial()));
0 ignored issues
show
Bug introduced by
The method factorial() 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

174
        return $n->factorial()->divide($k->factorial()->multiply($n->subtract($k)->/** @scrutinizer ignore-call */ factorial()));

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...
175
176
    }
177
178
    /**
179
     * @param     $z
180
     * @param int $precision
181
     *
182
     * @return DecimalInterface|NumberInterface|ImmutableNumber
183
     * @throws IntegrityConstraint
184
     */
185
    public static function gammaFunction($z, int $precision = 10)
186
    {
187
        $z = Numbers::makeOrDont(Numbers::IMMUTABLE, $z);
188
189
        if ($z->isInt()) {
190
            if ($z->isNegative() || $z->isEqual(0)) {
191
                throw new IntegrityConstraint(
192
                    'Non-positive integers are not valid gamma function arguments',
193
                    'Do not provide non-positive integers to this function',
194
                    'The gamma function is not defined for zero or negative integers, but is continuous for all other values'
195
                );
196
            }
197
            return $z->subtract(1)->factorial();
198
        }
199
200
        $x = Numbers::makeZero();
201
        $e = Numbers::makeE();
202
        $gamma = Numbers::makeZero();
203
204
        $continue = true;
205
206
        while ($continue) {
207
208
            $adjustment = $x->pow(
209
                $z->subtract(1)
210
            )->multiply(
211
                $e->pow(
212
                    $x->multiply(-1)
213
                )
214
            );
215
216
            $gamma = $gamma->add($adjustment);
217
218
            if ($adjustment->numberOfLeadingZeros() > $precision) {
219
                $continue = false;
220
            }
221
222
        }
223
224
        return $gamma;
225
    }
226
227
}