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
|
|
|
} |