Passed
Branch master (7fff96)
by Jordan
06:01
created

ConstantProvider   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 207
Duplicated Lines 0 %

Test Coverage

Coverage 95.06%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 81
dl 0
loc 207
ccs 77
cts 81
cp 0.9506
rs 10
c 1
b 0
f 1
wmc 22

6 Methods

Rating   Name   Duplication   Size   Complexity  
A makeE() 0 31 6
A makeLn10() 0 13 3
A _makeLnSpecial() 0 19 2
A makeLn1p1() 0 16 3
A makeLn2() 0 14 3
A makePi() 0 41 5
1
<?php
2
3
4
namespace Samsara\Fermat\Provider;
5
6
7
use Samsara\Exceptions\SystemError\PlatformError\MissingPackage;
8
use Samsara\Exceptions\UsageError\IntegrityConstraint;
9
use Samsara\Fermat\Enums\CalcMode;
10
use Samsara\Fermat\Enums\NumberBase;
11
use Samsara\Fermat\Numbers;
12
use Samsara\Fermat\Types\Base\Interfaces\Numbers\DecimalInterface;
13
use Samsara\Fermat\Values\ImmutableDecimal;
14
15
/**
16
 *
17
 */
18
class ConstantProvider
19
{
20
21
    private static DecimalInterface $pi;
22
    private static DecimalInterface $e;
23
    private static DecimalInterface $ln10;
24
    private static DecimalInterface $ln2;
25
    private static DecimalInterface $ln1p1;
26
27
    /**
28
     * @param int $digits
29
     * @return string
30
     * @throws IntegrityConstraint
31
     * @throws MissingPackage
32
     */
33 3
    public static function makePi(int $digits): string
34
    {
35
36 3
        if (isset(self::$pi) && self::$pi->numberOfDecimalDigits() >= $digits) {
37
            return self::$pi->truncateToScale($digits)->getValue(NumberBase::Ten);
0 ignored issues
show
Unused Code introduced by
The call to Samsara\Fermat\Types\Bas...erInterface::getValue() has too many arguments starting with Samsara\Fermat\Enums\NumberBase::Ten. ( Ignorable by Annotation )

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

37
            return self::$pi->truncateToScale($digits)->/** @scrutinizer ignore-call */ getValue(NumberBase::Ten);

This check compares calls to functions or methods with their respective definitions. If the call has more 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...
38
        }
39
40 3
        $internalScale = ($digits*2) + 10;
41
42 3
        $C = Numbers::make(Numbers::IMMUTABLE, '10005', $internalScale)->setMode(CalcMode::Precision)->sqrt($internalScale)->multiply(426880);
43 3
        $M = Numbers::make(Numbers::IMMUTABLE, '1', $internalScale)->setMode(CalcMode::Precision);
44 3
        $L = Numbers::make(Numbers::IMMUTABLE, '13591409', $internalScale)->setMode(CalcMode::Precision);
45 3
        $K = Numbers::make(Numbers::IMMUTABLE, '6', $internalScale)->setMode(CalcMode::Precision);
46 3
        $X = Numbers::make(Numbers::IMMUTABLE, '1')->setMode(CalcMode::Precision);
47 3
        $sum = Numbers::make(Numbers::MUTABLE,'0', $internalScale + 2)->setMode(CalcMode::Precision);
48 3
        $termNum = 0;
49 3
        $one = Numbers::makeOne($internalScale)->setMode(CalcMode::Precision);
50
51 3
        $continue = true;
52
53 3
        while ($continue) {
54 3
            $term = $M->multiply($L)->divide($X, $internalScale);
55
56 3
            if ($termNum > $internalScale) {
57 3
                $continue = false;
58
            }
59
60 3
            $sum->add($term);
61
62 3
            $M = $M->multiply($K->pow(3)->subtract($K->multiply(16))->divide($one->add($termNum)->pow(3), $internalScale));
63 3
            $L = $L->add(545140134);
64 3
            $X = $X->multiply('-262537412640768000');
65 3
            $K = $K->add(12);
66 3
            $termNum++;
67
        }
68
69 3
        $pi = $C->divide($sum, $internalScale);
70
71 3
        self::$pi = $pi->truncateToScale($digits);
72
73 3
        return $pi->truncateToScale($digits)->getValue(NumberBase::Ten);
74
75
    }
76
77
    /**
78
     * Consider also: sum [0 -> INF] { (2n + 2) / (2n + 1)! }
79
     *
80
     * This converges faster (though it's unclear if the calculation is actually faster), and can be represented by this
81
     * set of Fermat calls:
82
     *
83
     * SequenceProvider::nthEvenNumber($n + 1)->divide(SequenceProvider::nthOddNumber($n)->factorial());
84
     *
85
     * Perhaps by substituting the nthOddNumber()->factorial() call with something tracked locally, the performance can
86
     * be improved. Current performance is acceptable even out past 200 digits.
87
     *
88
     * @param int $digits
89
     * @return string
90
     * @throws IntegrityConstraint
91
     */
92 2
    public static function makeE(int $digits): string
93
    {
94
95 2
        if (isset(self::$e) && self::$e->numberOfDecimalDigits() >= $digits) {
96
            return self::$e->truncateToScale($digits)->getValue(NumberBase::Ten);
0 ignored issues
show
Unused Code introduced by
The call to Samsara\Fermat\Types\Bas...erInterface::getValue() has too many arguments starting with Samsara\Fermat\Enums\NumberBase::Ten. ( Ignorable by Annotation )

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

96
            return self::$e->truncateToScale($digits)->/** @scrutinizer ignore-call */ getValue(NumberBase::Ten);

This check compares calls to functions or methods with their respective definitions. If the call has more 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...
97
        }
98
99 2
        $internalScale = $digits + 3;
100
101 2
        $one = Numbers::makeOne($internalScale+5)->setMode(CalcMode::Precision);
102 2
        $denominator = Numbers::make(Numbers::MUTABLE, '1', $internalScale)->setMode(CalcMode::Precision);
103 2
        $e = Numbers::make(NUmbers::MUTABLE, '2', $internalScale)->setMode(CalcMode::Precision);
104 2
        $n = Numbers::make(Numbers::MUTABLE, '2', $internalScale)->setMode(CalcMode::Precision);
105
106 2
        $continue = true;
107
108 2
        while ($continue) {
109 2
            $denominator->multiply($n);
110 2
            $n->add($one);
111 2
            $term = $one->divide($denominator);
112
113 2
            if ($term->numberOfLeadingZeros() > $internalScale || $term->isEqual(0)) {
0 ignored issues
show
Bug introduced by
The method numberOfLeadingZeros() does not exist on Samsara\Fermat\Types\Fraction. ( Ignorable by Annotation )

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

113
            if ($term->/** @scrutinizer ignore-call */ numberOfLeadingZeros() > $internalScale || $term->isEqual(0)) {

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...
114 2
                $continue = false;
115
            }
116
117 2
            $e->add($term);
118
        }
119
120 2
        self::$e = $e->truncateToScale($digits);
0 ignored issues
show
Bug introduced by
The method truncateToScale() does not exist on Samsara\Fermat\Values\MutableFraction. ( Ignorable by Annotation )

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

120
        /** @scrutinizer ignore-call */ 
121
        self::$e = $e->truncateToScale($digits);

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 truncateToScale() does not exist on Samsara\Fermat\Values\ImmutableFraction. ( Ignorable by Annotation )

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

120
        /** @scrutinizer ignore-call */ 
121
        self::$e = $e->truncateToScale($digits);

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...
121
122 2
        return $e->truncateToScale($digits)->getValue(NumberBase::Ten);
123
124
    }
125
126
    /**
127
     * The lnScale() implementation is very efficient, so this is probably our best bet for computing more digits of
128
     * ln(10) to provide.
129
     *
130
     * @param int $digits
131
     * @return string
132
     * @throws IntegrityConstraint
133
     */
134 1
    public static function makeLn10(int $digits): string
135
    {
136
137 1
        if (isset(self::$ln10) && self::$ln10->numberOfDecimalDigits() >= $digits) {
138
            return self::$ln10->truncateToScale($digits)->getValue(NumberBase::Ten);
0 ignored issues
show
Unused Code introduced by
The call to Samsara\Fermat\Types\Bas...erInterface::getValue() has too many arguments starting with Samsara\Fermat\Enums\NumberBase::Ten. ( Ignorable by Annotation )

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

138
            return self::$ln10->truncateToScale($digits)->/** @scrutinizer ignore-call */ getValue(NumberBase::Ten);

This check compares calls to functions or methods with their respective definitions. If the call has more 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...
139
        }
140
141 1
        $ln10 = Numbers::make(Numbers::IMMUTABLE, 10, $digits+2)->setMode(CalcMode::Precision);
142 1
        $ln10 = $ln10->ln();
0 ignored issues
show
Bug introduced by
The method ln() does not exist on Samsara\Fermat\Values\ImmutableFraction. ( Ignorable by Annotation )

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

142
        /** @scrutinizer ignore-call */ 
143
        $ln10 = $ln10->ln();

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 ln() does not exist on Samsara\Fermat\Values\MutableFraction. ( Ignorable by Annotation )

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

142
        /** @scrutinizer ignore-call */ 
143
        $ln10 = $ln10->ln();

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...
143
144 1
        self::$ln10 = $ln10;
145
146 1
        return $ln10->truncateToScale($digits)->getValue(NumberBase::Ten);
147
148
    }
149
150
    /**
151
     * This function is a special case of the ln() function where x can be represented by (n + 1)/n, where n is an
152
     * integer. This particular special case converges extremely rapidly. For ln(2), n = 1.
153
     *
154
     * @param int $digits
155
     * @return string
156
     */
157 10
    public static function makeLn2(int $digits): string
158
    {
159
160 10
        if (isset(self::$ln2) && self::$ln2->numberOfDecimalDigits() >= $digits) {
161 6
            return self::$ln2->truncateToScale($digits)->getValue(NumberBase::Ten);
0 ignored issues
show
Unused Code introduced by
The call to Samsara\Fermat\Types\Bas...erInterface::getValue() has too many arguments starting with Samsara\Fermat\Enums\NumberBase::Ten. ( Ignorable by Annotation )

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

161
            return self::$ln2->truncateToScale($digits)->/** @scrutinizer ignore-call */ getValue(NumberBase::Ten);

This check compares calls to functions or methods with their respective definitions. If the call has more 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...
162
        }
163
164 4
        $twoThirds = Numbers::make(Numbers::IMMUTABLE, str_pad('0.', $digits+3, '6'));
165 4
        $nine = Numbers::make(Numbers::IMMUTABLE, 9, $digits+3);
166 4
        $ln2 = self::_makeLnSpecial($digits, $nine, $twoThirds);
0 ignored issues
show
Bug introduced by
It seems like $twoThirds can also be of type Samsara\Fermat\Values\ImmutableFraction and Samsara\Fermat\Values\MutableDecimal and Samsara\Fermat\Values\MutableFraction; however, parameter $outerNum of Samsara\Fermat\Provider\...vider::_makeLnSpecial() does only seem to accept Samsara\Fermat\Values\ImmutableDecimal, maybe add an additional type check? ( Ignorable by Annotation )

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

166
        $ln2 = self::_makeLnSpecial($digits, $nine, /** @scrutinizer ignore-type */ $twoThirds);
Loading history...
Bug introduced by
It seems like $nine can also be of type Samsara\Fermat\Values\ImmutableFraction and Samsara\Fermat\Values\MutableDecimal and Samsara\Fermat\Values\MutableFraction; however, parameter $innerNum of Samsara\Fermat\Provider\...vider::_makeLnSpecial() does only seem to accept Samsara\Fermat\Values\ImmutableDecimal, maybe add an additional type check? ( Ignorable by Annotation )

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

166
        $ln2 = self::_makeLnSpecial($digits, /** @scrutinizer ignore-type */ $nine, $twoThirds);
Loading history...
167
168 4
        self::$ln2 = $ln2;
169
170 4
        return $ln2->getValue(NumberBase::Ten);
171
172
    }
173
174
    /**
175
     * This function is a special case of the ln() function where x can be represented by (n + 1)/n, where n is an
176
     * integer. This particular special case converges extremely rapidly. For ln(1.1), n = 10.
177
     *
178
     * @param int $digits
179
     * @return string
180
     */
181 42
    public static function makeLn1p1(int $digits): string
182
    {
183
184 42
        if (isset(self::$ln1p1) && self::$ln1p1->numberOfDecimalDigits() >= $digits) {
185 40
            return self::$ln1p1->truncateToScale($digits)->getValue(NumberBase::Ten);
0 ignored issues
show
Unused Code introduced by
The call to Samsara\Fermat\Types\Bas...erInterface::getValue() has too many arguments starting with Samsara\Fermat\Enums\NumberBase::Ten. ( Ignorable by Annotation )

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

185
            return self::$ln1p1->truncateToScale($digits)->/** @scrutinizer ignore-call */ getValue(NumberBase::Ten);

This check compares calls to functions or methods with their respective definitions. If the call has more 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...
186
        }
187
188 3
        $two = Numbers::make(Numbers::IMMUTABLE, 2, $digits+3);
189 3
        $twentyOne = Numbers::make(Numbers::IMMUTABLE, 21, $digits+3);
190 3
        $fourFortyOne = Numbers::make(Numbers::IMMUTABLE, 441, $digits+3);
191 3
        $twoDivTwentyOne = $two->divide($twentyOne);
192 3
        $ln1p1 = self::_makeLnSpecial($digits, $fourFortyOne, $twoDivTwentyOne);
0 ignored issues
show
Bug introduced by
It seems like $fourFortyOne can also be of type Samsara\Fermat\Values\ImmutableFraction and Samsara\Fermat\Values\MutableDecimal and Samsara\Fermat\Values\MutableFraction; however, parameter $innerNum of Samsara\Fermat\Provider\...vider::_makeLnSpecial() does only seem to accept Samsara\Fermat\Values\ImmutableDecimal, maybe add an additional type check? ( Ignorable by Annotation )

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

192
        $ln1p1 = self::_makeLnSpecial($digits, /** @scrutinizer ignore-type */ $fourFortyOne, $twoDivTwentyOne);
Loading history...
Bug introduced by
It seems like $twoDivTwentyOne can also be of type Samsara\Fermat\Types\Fraction and Samsara\Fermat\Values\ImmutableFraction and Samsara\Fermat\Values\MutableDecimal and Samsara\Fermat\Values\MutableFraction; however, parameter $outerNum of Samsara\Fermat\Provider\...vider::_makeLnSpecial() does only seem to accept Samsara\Fermat\Values\ImmutableDecimal, maybe add an additional type check? ( Ignorable by Annotation )

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

192
        $ln1p1 = self::_makeLnSpecial($digits, $fourFortyOne, /** @scrutinizer ignore-type */ $twoDivTwentyOne);
Loading history...
193
194 3
        self::$ln1p1 = $ln1p1;
195
196 3
        return $ln1p1->getValue(NumberBase::Ten);
197
198
    }
199
200
    /**
201
     * @param int $digits
202
     * @param ImmutableDecimal $innerNum
203
     * @param ImmutableDecimal $outerNum
204
     * @return ImmutableDecimal
205
     */
206 6
    private static function _makeLnSpecial(int $digits, ImmutableDecimal $innerNum, ImmutableDecimal $outerNum): ImmutableDecimal
207
    {
208 6
        $one = Numbers::makeOne($digits+3);
209 6
        $two = Numbers::make(Numbers::IMMUTABLE, 2, $digits+3);
210 6
        $sum = Numbers::makeZero($digits+3);
211 6
        $k = 0;
212
213
        do {
214
215 6
            $diff = $one->divide($one->add($two->multiply($k))->multiply($innerNum->pow($k)), $digits+3)->truncate($digits+2);
0 ignored issues
show
Bug introduced by
The method truncate() does not exist on Samsara\Fermat\Types\Fraction. ( Ignorable by Annotation )

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

215
            $diff = $one->divide($one->add($two->multiply($k))->multiply($innerNum->pow($k)), $digits+3)->/** @scrutinizer ignore-call */ truncate($digits+2);

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...
216
217 6
            $sum = $sum->add($diff);
218
219 6
            $k++;
220
221 6
        } while (!$diff->isEqual(0));
222
223 6
        $lnSpecial = $outerNum->multiply($sum);
224 6
        return $lnSpecial->truncateToScale($digits);
225
    }
226
227
}