StatsProvider::complementNormalCDF()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Samsara\Fermat\Provider;
4
5
use Ds\Vector;
6
use ReflectionException;
7
use Samsara\Exceptions\UsageError\IntegrityConstraint;
8
use Samsara\Exceptions\SystemError\LogicalError\IncompatibleObjectState;
9
use Samsara\Exceptions\UsageError\OptionalExit;
10
use Samsara\Fermat\Numbers;
11
use Samsara\Fermat\Types\Base\Interfaces\Numbers\NumberInterface;
12
use Samsara\Fermat\Types\Base\Interfaces\Numbers\DecimalInterface;
13
use Samsara\Fermat\Types\Base\Interfaces\Numbers\FractionInterface;
14
use Samsara\Fermat\Values\ImmutableDecimal;
15
use Samsara\Fermat\Values\ImmutableFraction;
16
17
class StatsProvider
18
{
19
20
    /**
21
     * @var Vector
22
     */
23
    protected static $inverseErrorCoefs;
24
25
    /**
26
     * @param $x
27
     *
28
     * @return NumberInterface
29
     * @throws IntegrityConstraint
30
     * @throws OptionalExit|ReflectionException
31
     */
32 2
    public static function normalCDF($x): ImmutableDecimal
33
    {
34 2
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
35
36 2
        $scale = $x->getScale();
37 2
        $internalScale = $scale+2;
38
39 2
        $pi = Numbers::makePi($internalScale);
40 2
        $e = Numbers::makeE($internalScale);
41 2
        $one = Numbers::makeOne($internalScale);
42
43 2
        $eExponent = Numbers::make(Numbers::IMMUTABLE, $x->getValue());
44 2
        $eExponent = $eExponent->pow(2)->divide(2)->multiply(-1);
45
46 2
        $answer = Numbers::make(Numbers::IMMUTABLE, 0.5);
47 2
        $answer = $answer->add(
48 2
            $one->divide($pi->multiply(2)->sqrt())
49 2
                ->multiply($e->pow($eExponent))
50 2
                ->multiply(SeriesProvider::maclaurinSeries(
51 2
                    $x,
52 2
                    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

52
                    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...
53 2
                        return Numbers::makeOne();
54 2
                    },
55 2
                    function ($n) {
56 2
                        return SequenceProvider::nthOddNumber($n);
57 2
                    },
58 2
                    function ($n) {
59 2
                        return SequenceProvider::nthOddNumber($n)->doubleFactorial();
0 ignored issues
show
Bug introduced by
The method doubleFactorial() does not exist on Samsara\Fermat\Types\Bas...Numbers\NumberInterface. It seems like you code against a sub-type of Samsara\Fermat\Types\Bas...Numbers\NumberInterface such as Samsara\Fermat\Types\Bas...umbers\DecimalInterface or Samsara\Fermat\Types\Decimal. ( Ignorable by Annotation )

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

59
                        return SequenceProvider::nthOddNumber($n)->/** @scrutinizer ignore-call */ doubleFactorial();
Loading history...
Bug introduced by
The method doubleFactorial() does not exist on Samsara\Fermat\Types\NumberCollection. ( Ignorable by Annotation )

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

59
                        return SequenceProvider::nthOddNumber($n)->/** @scrutinizer ignore-call */ doubleFactorial();

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...
60 2
                    },
61 2
                    0,
62
                    $internalScale
63
                ))
64
        );
65
66 2
        return $answer->truncateToScale($scale);
67
68
    }
69
70
    /**
71
     * @param $x
72
     *
73
     * @return DecimalInterface|NumberInterface
74
     * @throws IntegrityConstraint
75
     * @throws OptionalExit
76
     */
77 1
    public static function complementNormalCDF($x): ImmutableDecimal
78
    {
79 1
        $p = self::normalCDF($x);
80 1
        $one = Numbers::makeOne();
81
82 1
        return $one->subtract($p);
83
    }
84
85
    /**
86
     * @param $x
87
     *
88
     * @return DecimalInterface|FractionInterface|NumberInterface|ImmutableDecimal
89
     * @throws IntegrityConstraint
90
     * @throws OptionalExit
91
     */
92 5
    public static function gaussErrorFunction($x): ImmutableDecimal
93
    {
94
95 5
        $x = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
96
97 5
        $scale = $x->getScale();
98 5
        $internalScale = $scale + 2;
99
100 5
        $answer = Numbers::makeOne($internalScale);
101 5
        $pi = Numbers::makePi($internalScale);
102
103 5
        $answer = $answer->multiply(2)->divide($pi->sqrt());
104
105 5
        $answer = $answer->multiply(
106 5
            SeriesProvider::maclaurinSeries(
107 5
                $x,
108 5
                function ($n) {
109 5
                    $negOne = Numbers::make(Numbers::IMMUTABLE, -1);
110
111 5
                    return $negOne->pow($n);
112 5
                },
113 5
                function ($n) {
114 5
                    return SequenceProvider::nthOddNumber($n);
115 5
                },
116 5
                function ($n) {
117 5
                    $n = Numbers::makeOrDont(Numbers::IMMUTABLE, $n);
118
119 5
                    return $n->factorial()->multiply(SequenceProvider::nthOddNumber($n->asInt()));
0 ignored issues
show
Bug introduced by
The method asInt() does not exist on Samsara\Fermat\Types\Bas...Numbers\NumberInterface. It seems like you code against a sub-type of Samsara\Fermat\Types\Bas...Numbers\NumberInterface such as Samsara\Fermat\Types\Bas...umbers\DecimalInterface or Samsara\Fermat\Types\Decimal. ( Ignorable by Annotation )

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

119
                    return $n->factorial()->multiply(SequenceProvider::nthOddNumber($n->/** @scrutinizer ignore-call */ asInt()));
Loading history...
Bug introduced by
The method factorial() does not exist on Samsara\Fermat\Types\Bas...Numbers\NumberInterface. It seems like you code against a sub-type of Samsara\Fermat\Types\Bas...Numbers\NumberInterface such as Samsara\Fermat\Types\Bas...umbers\DecimalInterface or Samsara\Fermat\Types\Decimal. ( Ignorable by Annotation )

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

119
                    return $n->/** @scrutinizer ignore-call */ factorial()->multiply(SequenceProvider::nthOddNumber($n->asInt()));
Loading history...
120 5
                },
121 5
                0,
122
                $internalScale
123
            )
124
        );
125
126 5
        return $answer->truncateToScale($scale);
127
128
    }
129
130
    /**
131
     * @param     $p
132
     * @param int $scale
133
     *
134
     * @return DecimalInterface|NumberInterface|ImmutableDecimal
135
     * @throws IntegrityConstraint
136
     * @throws OptionalExit
137
     */
138 2
    public static function inverseNormalCDF($p, int $scale = 10): ImmutableDecimal
139
    {
140 2
        $p = Numbers::makeOrDont(Numbers::IMMUTABLE, $p);
141
142 2
        $scale = $scale ?? $p->getScale();
143 2
        $internalScale = $scale + 2;
144
145 2
        $two = Numbers::make(Numbers::IMMUTABLE, 2, $internalScale);
146 2
        $invErfArg = $two->multiply($p)->subtract(1);
147
148 2
        return StatsProvider::inverseGaussErrorFunction($invErfArg, $internalScale)->multiply($two->sqrt($internalScale))->roundToScale($scale);
149
    }
150
151
    /**
152
     * @param $n
153
     * @param $k
154
     *
155
     * @return DecimalInterface|NumberInterface|ImmutableDecimal
156
     * @throws IntegrityConstraint
157
     * @throws IncompatibleObjectState
158
     */
159 4
    public static function binomialCoefficient($n, $k): ImmutableDecimal
160
    {
161
162 4
        $n = Numbers::makeOrDont(Numbers::IMMUTABLE, $n);
163 4
        $k = Numbers::makeOrDont(Numbers::IMMUTABLE, $k);
164
165 4
        if ($k->isLessThan(0) || $n->isLessThan($k)) {
0 ignored issues
show
Bug introduced by
The method isLessThan() does not exist on Samsara\Fermat\Types\Bas...Numbers\NumberInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Samsara\Fermat\Types\Base\Number. Are you sure you never get one of those? ( Ignorable by Annotation )

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

165
        if ($k->/** @scrutinizer ignore-call */ isLessThan(0) || $n->isLessThan($k)) {
Loading history...
166 2
            throw new IntegrityConstraint(
167 2
                '$k must be larger or equal to 0 and less than or equal to $n',
168 2
                'Provide valid $n and $k values such that 0 <= $k <= $n',
169 2
                'For $n choose $k, the values of $n and $k must satisfy the inequality 0 <= $k <= $n'
170
            );
171
        }
172
173 2
        if (!$n->isInt() || !$k->isInt()) {
0 ignored issues
show
Bug introduced by
The method isInt() does not exist on Samsara\Fermat\Types\Bas...Numbers\NumberInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Samsara\Fermat\Types\Bas...s\SimpleNumberInterface or Samsara\Fermat\Types\Base\Number or Samsara\Fermat\Types\Bas...mbers\FractionInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

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

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

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

181
        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...
182
183
    }
184
185 4
    public static function inverseErrorCoefficients(int $termIndex): ImmutableFraction
186
    {
187
188 4
        $terms =& static::$inverseErrorCoefs;
189
190 4
        if (is_null(static::$inverseErrorCoefs)) {
191 4
            $terms = new Vector();
192 4
            $terms->push(new ImmutableFraction(Numbers::makeOne(), Numbers::makeOne()));
193 4
            $terms->push(new ImmutableFraction(Numbers::makeOne(), Numbers::makeOne()));
194
        }
195
196 4
        if ($terms->offsetExists($termIndex)) {
0 ignored issues
show
Bug introduced by
The method offsetExists() does not exist on Ds\Vector. ( Ignorable by Annotation )

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

196
        if ($terms->/** @scrutinizer ignore-call */ offsetExists($termIndex)) {

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...
197 3
            return $terms->get($termIndex);
198
        }
199
200 4
        $nextTerm = $terms->count();
201
202 4
        for ($k = $nextTerm;$k <= $termIndex;$k++) {
203 4
            $termValue = new ImmutableFraction(new ImmutableDecimal('0'), new ImmutableDecimal('1'));
204 4
            for ($m = 0;$m <= ($k - 1);$m++) {
205 4
                $part1 = $terms->get($m);
206 4
                $part2 = $terms->get($k - 1 - $m);
207 4
                $part3 = $part1->multiply($part2);
208 4
                $part4 = ($m + 1)*($m*2 + 1);
209 4
                $part5 = $part3->divide($part4);
210 4
                $termValue = $termValue->add($part5);
211
            }
212
213 4
            $termValue = $termValue->simplify();
214
215 4
            $terms->push($termValue);
216
        }
217
218 4
        return $terms->get($termIndex);
219
220
    }
221
222
    /**
223
     * @param $z
224
     * @param int $scale
225
     *
226
     * @return ImmutableDecimal
227
     * @throws IntegrityConstraint
228
     * @throws OptionalExit
229
     * @throws ReflectionException
230
     */
231 3
    public static function inverseGaussErrorFunction($z, int $scale = 10): ImmutableDecimal
232
    {
233
234 3
        $z = Numbers::makeOrDont(Numbers::IMMUTABLE, $z);
235
236 3
        $scale = $scale ?? $z->getScale();
237 3
        $internalScale = $scale + 1;
238
239 3
        $pi = Numbers::makePi($internalScale);
240
241 3
        $answer = SeriesProvider::maclaurinSeries(
242 3
            $z,
243 3
            static function ($n) use ($pi) {
244 3
                if ($n > 0) {
245 3
                    return $pi->pow($n)->multiply(StatsProvider::inverseErrorCoefficients($n));
246
                }
247
248 3
                return Numbers::makeOne();
249 3
            },
250 3
            static function ($n) {
251 3
                return SequenceProvider::nthOddNumber($n);
252 3
            },
253 3
            static function ($n) {
254 3
                if ($n > 0) {
255 3
                    $extra = Numbers::make(Numbers::IMMUTABLE, 2)->pow(SequenceProvider::nthEvenNumber($n));
256
                } else {
257 3
                    $extra = Numbers::makeOne();
258
                }
259
260 3
                return SequenceProvider::nthOddNumber($n)->multiply($extra);
261 3
            },
262 3
            0,
263
            $internalScale
264
        );
265
266 3
        $answer = $answer->multiply($pi->sqrt($internalScale)->divide(2, $internalScale));
267
268 3
        return $answer->roundToScale($scale);
269
270
    }
271
272
}