Passed
Pull Request — dev (#56)
by Jordan
08:50
created

ConstantProvider::makeE()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 4
eloc 14
c 1
b 0
f 1
nc 3
nop 1
dl 0
loc 25
ccs 15
cts 15
cp 1
crap 4
rs 9.7998
1
<?php
2
3
4
namespace Samsara\Fermat\Provider;
5
6
7
use Samsara\Exceptions\UsageError\IntegrityConstraint;
8
use Samsara\Fermat\Numbers;
9
use Samsara\Fermat\Types\Base\Selectable;
10
11
class ConstantProvider
12
{
13
14 3
    public static function makePi(int $digits): string
15
    {
16
17 3
        $internalPrecision = $digits + 10;
18
19 3
        $C = Numbers::make(Numbers::IMMUTABLE, '10005', $internalPrecision)->sqrt($internalPrecision)->multiply(426880);
20 3
        $M = Numbers::make(Numbers::IMMUTABLE, '1', $internalPrecision);
21 3
        $L = Numbers::make(Numbers::IMMUTABLE, '13591409', $internalPrecision);
22 3
        $K = Numbers::make(Numbers::IMMUTABLE, '6', $internalPrecision);
23 3
        $X = Numbers::make(Numbers::IMMUTABLE, '1');
24 3
        $sum = Numbers::make(Numbers::MUTABLE,'0', $internalPrecision + 2);
25 3
        $termNum = 0;
26 3
        $one = Numbers::makeOne($internalPrecision);
27
28 3
        $continue = true;
29
30 3
        while ($continue) {
31 3
            $term = $M->multiply($L)->divide($X);
32
33 3
            if ($term->numberOfLeadingZeros() > $internalPrecision || $term->isEqual(0)) {
34 3
                $continue = false;
35
            }
36
37 3
            $sum->add($term);
38
39 3
            $M = $M->multiply($K->pow(3)->subtract($K->multiply(16))->divide($one->add($termNum)->pow(3), $internalPrecision));
40 3
            $L = $L->add(545140134);
41 3
            $X = $X->multiply('-262537412640768000');
42 3
            $K = $K->add(12);
43 3
            $termNum++;
44
        }
45
46 3
        $pi = $C->divide($sum, $internalPrecision);
47
48 3
        return $pi->truncateToPrecision($digits)->getValue();
49
50
    }
51
52
    /**
53
     * Consider also: sum [0 -> INF] { (2n + 2) / (2n + 1)! }
54
     *
55
     * This converges faster (though it's unclear if the calculation is actually faster), and can be represented by this
56
     * set of Fermat calls:
57
     *
58
     * SequenceProvider::nthEvenNumber($n + 1)->divide(SequenceProvider::nthOddNumber($n)->factorial());
59
     *
60
     * Perhaps by substituting the nthOddNumber()->factorial() call with something tracked locally, the performance can
61
     * be improved. Current performance is acceptable even out past 200 digits.
62
     *
63
     * @param int $digits
64
     * @return string
65
     * @throws IntegrityConstraint
66
     */
67 2
    public static function makeE(int $digits): string
68
    {
69
70 2
        $internalPrecision = $digits + 3;
71
72 2
        $one = Numbers::makeOne($internalPrecision+5)->setMode(Selectable::CALC_MODE_PRECISION);
73 2
        $denominator = Numbers::make(Numbers::MUTABLE, '1', $internalPrecision)->setMode(Selectable::CALC_MODE_PRECISION);
74 2
        $e = Numbers::make(NUmbers::MUTABLE, '2', $internalPrecision)->setMode(Selectable::CALC_MODE_PRECISION);
75 2
        $n = Numbers::make(Numbers::MUTABLE, '2', $internalPrecision)->setMode(Selectable::CALC_MODE_PRECISION);
76
77 2
        $continue = true;
78
79 2
        while ($continue) {
80 2
            $denominator->multiply($n);
81 2
            $n->add($one);
82 2
            $term = $one->divide($denominator);
83
84 2
            if ($term->numberOfLeadingZeros() > $internalPrecision || $term->isEqual(0)) {
85 2
                $continue = false;
86
            }
87
88 2
            $e->add($term);
89
        }
90
91 2
        return $e->truncateToPrecision($digits)->getValue();
92
93
    }
94
95
}