Failed Conditions
Pull Request — dev (#50)
by Jordan
09:06 queued 04:26
created

SeriesProvider::genericTwoPartSeries()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 30
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 3
nop 5
dl 0
loc 30
ccs 0
cts 13
cp 0
crap 12
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace Samsara\Fermat\Provider;
4
5
use ReflectionException;
6
use Samsara\Exceptions\UsageError\IntegrityConstraint;
7
use Samsara\Exceptions\UsageError\OptionalExit;
8
use Samsara\Fermat\Numbers;
9
use Samsara\Fermat\Types\Base\Interfaces\Numbers\SimpleNumberInterface;
10
use Samsara\Fermat\Values\ImmutableDecimal;
11
12
class SeriesProvider
13
{
14
15
    /**
16
     * Creates a series that evaluates the following:
17
     *
18
     * SUM[$startTerm -> infinity](
19
     *  $numerator($n) × $input^$exponent($n)
20
     *  --------------------------------
21
     *          $denominator($n)
22
     * )
23
     *
24
     * Where $n is the current term number, starting at $startTerm, and increasing by 1 each loop; where $numerator,
25
     * $exponent, and $denominator are callables that take the term number (as an int) as their only input, and give the
26
     * value of that section at that term number; and where $input is the x value being considered for the series.
27
     *
28
     * The function continues adding terms until a term has MORE leading zeros than the $precision setting. (That is,
29
     * until it adds zero to the total when considering significant digits.)
30
     *
31
     * @param SimpleNumberInterface $input
32
     * @param callable $numerator
33
     * @param callable $exponent
34
     * @param callable $denominator
35
     * @param int $startTermAt
36
     * @param int $precision
37
     * @param int $consecutiveDivergeLimit
38
     * @param int $totalDivergeLimit
39
     *
40
     * @return ImmutableDecimal
41
     * @throws IntegrityConstraint
42
     * @throws OptionalExit
43
     * @throws ReflectionException
44
     */
45 36
    public static function maclaurinSeries(
46
        SimpleNumberInterface $input, // x value in series
47
        callable $numerator, // a function determining what the sign (+/-) is at the nth term
48
        callable $exponent, // a function determining the exponent of x at the nth term
49
        callable $denominator, // a function determining the denominator at the nth term
50
        int $startTermAt = 0,
51
        int $precision = 10,
52
        int $consecutiveDivergeLimit = 5,
53
        int $totalDivergeLimit = 10): ImmutableDecimal
54
    {
55
56 36
        ++$precision;
57
58 36
        $sum = Numbers::makeZero($precision);
59 36
        $value = Numbers::make(Numbers::IMMUTABLE, $input->getValue(), $precision);
60
61 36
        $continue = true;
62 36
        $termNumber = $startTermAt;
63
64 36
        $adjustmentOfZero = 0;
65 36
        $prevDiff = Numbers::makeZero($precision);
66 36
        $prevSum = $sum;
67 36
        $divergeCount = -1;
68 36
        $persistentDivergeCount = -1;
69 36
        $currentPrecision = 0;
70
71 36
        while ($continue) {
72 36
            $term = Numbers::makeOne($precision);
73
74
            try {
75 36
                $exTerm = $value->pow($exponent($termNumber));
76 36
                $term = $term->multiply($exTerm);
77 36
                $term = $term->divide($denominator($termNumber));
0 ignored issues
show
Bug introduced by
The method divide() does not exist on Samsara\Fermat\Types\Bas...tes\CoordinateInterface. It seems like you code against a sub-type of Samsara\Fermat\Types\Bas...tes\CoordinateInterface such as Samsara\Fermat\Types\ComplexNumber or Samsara\Fermat\Types\ComplexNumber. ( Ignorable by Annotation )

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

77
                /** @scrutinizer ignore-call */ 
78
                $term = $term->divide($denominator($termNumber));
Loading history...
Bug introduced by
The method divide() does not exist on Samsara\Fermat\Values\Ge...ems\CartesianCoordinate. ( Ignorable by Annotation )

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

77
                /** @scrutinizer ignore-call */ 
78
                $term = $term->divide($denominator($termNumber));

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...
78 36
                $term = $term->multiply($numerator($termNumber));
0 ignored issues
show
Bug introduced by
The method multiply() does not exist on Samsara\Fermat\Types\Bas...tes\CoordinateInterface. It seems like you code against a sub-type of Samsara\Fermat\Types\Bas...tes\CoordinateInterface such as Samsara\Fermat\Types\ComplexNumber or Samsara\Fermat\Types\ComplexNumber. ( Ignorable by Annotation )

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

78
                /** @scrutinizer ignore-call */ 
79
                $term = $term->multiply($numerator($termNumber));
Loading history...
Bug introduced by
The method multiply() does not exist on Samsara\Fermat\Values\Ge...ems\CartesianCoordinate. ( Ignorable by Annotation )

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

78
                /** @scrutinizer ignore-call */ 
79
                $term = $term->multiply($numerator($termNumber));

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...
79
            } catch (IntegrityConstraint $constraint) {
80
                return $sum->truncateToPrecision($currentPrecision+1);
81
            }
82
83
            /** @var ImmutableDecimal $term */
84 36
            if ($term->numberOfLeadingZeros() >= $precision && !$term->isWhole()) {
85 29
                $continue = false;
86
            }
87
88 36
            $currentPrecision = $term->numberOfLeadingZeros();
89
90 36
            if ($term->isEqual(0)) {
91 9
                $adjustmentOfZero++;
92
            } else {
93 36
                $adjustmentOfZero = 0;
94
            }
95
96 36
            if ($adjustmentOfZero > 15) {
97 9
                $continue = false;
98
            }
99
100
            /** @var ImmutableDecimal $sum */
101 36
            $sum = $sum->add($term);
102 36
            $currDiff = $sum->subtract($prevSum)->abs();
103
104 36
            if ($prevDiff->isLessThan($currDiff)) {
105 36
                $divergeCount++;
106 36
                $persistentDivergeCount++;
107
            } else {
108 36
                $divergeCount = 0;
109
            }
110
111 36
            if ($divergeCount === $consecutiveDivergeLimit || $persistentDivergeCount === $totalDivergeLimit) {
112
                throw new OptionalExit(
113
                    'Series appear to be diverging. Current diverge count: '.$divergeCount.' | Persistent diverge count: '.$persistentDivergeCount,
114
                    'A call was made to SeriesProvider::maclaurinSeries() that seems to be diverging. Exiting the loop.'
115
                );
116
            }
117
118 36
            $prevDiff = $currDiff;
119 36
            $prevSum = $sum;
120
121 36
            $termNumber++;
122
        }
123
124 36
        return $sum->roundToPrecision($precision);
125
126
    }
127
    
128
}