Passed
Pull Request — dev (#38)
by Jordan
02:02
created

StatsProvider::binomialCoefficient()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 3
nop 2
dl 0
loc 23
ccs 0
cts 14
cp 0
crap 30
rs 9.5222
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
                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
                function ($n) {
93 3
                    return SequenceProvider::nthOddNumber($n);
94 3
                },
95
                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(
129
                $r2pi->multiply(
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

129
                $r2pi->/** @scrutinizer ignore-call */ 
130
                       multiply(
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

129
                $r2pi->/** @scrutinizer ignore-call */ 
130
                       multiply(
Loading history...
130
                    $e->pow(
131
                        $xCur->pow(2)
132
                    )->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

132
                    )->/** @scrutinizer ignore-call */ 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

132
                    )->/** @scrutinizer ignore-call */ divide(-2)
Loading history...
133
                )
134
            );
135
            $xCur = $xCur->subtract($dx);
0 ignored issues
show
Bug introduced by
The method subtract() 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
            /** @scrutinizer ignore-call */ 
136
            $xCur = $xCur->subtract($dx);
Loading history...
136
137
            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

137
            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

137
            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...
138
                $continue = false;
139
            }
140
141
        }
142
143
        if ($p->isLessThan(0.5)) {
144
            return $xCur->multiply(-1);
145
        } else {
146
            return $xCur;
147
        }
148
    }
149
150
    /**
151
     * @param $n
152
     * @param $k
153
     *
154
     * @return DecimalInterface|NumberInterface|ImmutableNumber
155
     * @throws IntegrityConstraint
156
     * @throws IncompatibleObjectState
157
     */
158
    public static function binomialCoefficient($n, $k)
159
    {
160
161
        $n = Numbers::makeOrDont(Numbers::IMMUTABLE, $n);
162
        $k = Numbers::makeOrDont(Numbers::IMMUTABLE, $k);
163
164
        if ($k->isLessThan(0) || $n->isLessThan($k)) {
165
            throw new IntegrityConstraint(
166
                '$k must be larger or equal to 0 and less than or equal to $n',
167
                'Provide valid $n and $k values such that 0 <= $k <= $n',
168
                'For $n choose $k, the values of $n and $k must satisfy the inequality 0 <= $k <= $n'
169
            );
170
        }
171
172
        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

172
        if (!$n->/** @scrutinizer ignore-call */ isInt() || !$k->isInt()) {
Loading history...
173
            throw new IntegrityConstraint(
174
                '$k and $n must be whole numbers',
175
                'Provide whole numbers for $n and $k',
176
                'For $n choose $k, the values $n and $k must be whole numbers'
177
            );
178
        }
179
180
        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

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