Failed Conditions
Pull Request — master (#47)
by Jordan
06:42 queued 03:44
created

SeriesProvider::maclaurinSeries()   B

Complexity

Conditions 9
Paths 21

Size

Total Lines 81
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 9.1846

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 39
nc 21
nop 8
dl 0
loc 81
ccs 33
cts 38
cp 0.8684
crap 9.1846
rs 7.7404
c 1
b 0
f 0

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Samsara\Fermat\Provider;
4
5
use Samsara\Exceptions\UsageError\IntegrityConstraint;
6
use Samsara\Exceptions\UsageError\OptionalExit;
7
use Samsara\Fermat\Numbers;
8
use Samsara\Fermat\Types\Base\Interfaces\Numbers\DecimalInterface;
9
use Samsara\Fermat\Types\Base\Interfaces\Numbers\NumberInterface;
10
use Samsara\Fermat\Types\Base\Interfaces\Numbers\SimpleNumberInterface;
11
use Samsara\Fermat\Values\ImmutableDecimal;
12
13
class SeriesProvider
14
{
15
16
    /**
17
     * Creates a series that evaluates the following:
18
     *
19
     * SUM[$startTerm -> infinity](
20
     *  $numerator($n) × $input^$exponent($n)
21
     *  --------------------------------
22
     *          $denominator($n)
23
     * )
24
     *
25
     * Where $n is the current term number, starting at $startTerm, and increasing by 1 each loop; where $numerator,
26
     * $exponent, and $denominator are callables that take the term number (as an int) as their only input, and give the
27
     * value of that section at that term number; and where $input is the x value being considered for the series.
28
     *
29
     * The function continues adding terms until a term has MORE leading zeros than the $precision setting. (That is,
30
     * until it adds zero to the total when considering significant digits.)
31
     *
32
     * @param SimpleNumberInterface $input
33
     * @param callable              $numerator
34
     * @param callable              $exponent
35
     * @param callable              $denominator
36
     * @param int                   $startTermAt
37
     * @param int                   $precision
38
     * @param int                   $consecutiveDivergeLimit
39
     * @param int                   $totalDivergeLimit
40
     *
41
     * @return ImmutableDecimal
42
     * @throws IntegrityConstraint
43
     * @throws OptionalExit
44
     */
45 28
    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)
54
    {
55
56 28
        $precision += 1;
57
58 28
        $sum = Numbers::makeZero($precision);
59 28
        $value = Numbers::make(Numbers::IMMUTABLE, $input->getValue(), $precision);
60
61 28
        $continue = true;
62 28
        $termNumber = $startTermAt;
63
64 28
        $adjustmentOfZero = 0;
65 28
        $prevDiff = Numbers::makeZero($precision);
0 ignored issues
show
Unused Code introduced by
The assignment to $prevDiff is dead and can be removed.
Loading history...
66 28
        $prevSum = $sum;
67 28
        $divergeCount = -1;
68 28
        $persistentDivergeCount = -1;
69 28
        $currentPrecision = 0;
70
71 28
        while ($continue) {
72 28
            $term = Numbers::makeOne($precision);
73
74
            try {
75 28
                $exTerm = $value->pow($exponent($termNumber));
76 28
                $term = $term->multiply($exTerm);
77 28
                $term = $term->divide($denominator($termNumber));
78 28
                $term = $term->multiply($numerator($termNumber));
79
            } catch (IntegrityConstraint $constraint) {
80
                return $sum->truncateToPrecision($currentPrecision+1);
81
            }
82
83
            /** @var ImmutableDecimal $term */
84 28
            if ($term->numberOfLeadingZeros() >= $precision && !$term->isWhole()) {
85 21
                $continue = false;
86
            }
87
88 28
            $currentPrecision = $term->numberOfLeadingZeros();
89
90 28
            if ($term->isEqual(0)) {
91 7
                $adjustmentOfZero++;
92
            } else {
93 28
                $adjustmentOfZero = 0;
94
            }
95
96 28
            if ($adjustmentOfZero > 15) {
97 7
                $continue = false;
98
            }
99
100 28
            $sum = $sum->add($term);
101 28
            $currDiff = $sum->subtract($prevSum)->abs();
102
103
            /*
104
            if ($prevDiff->isLessThan($currDiff)) {
105
                $divergeCount++;
106
                $persistentDivergeCount++;
107
            } else {
108
                $divergeCount = 0;
109
            }
110
            */
111
112 28
            if ($divergeCount == $consecutiveDivergeLimit || $persistentDivergeCount == $totalDivergeLimit) {
113
                throw new OptionalExit(
114
                    'Series appear to be diverging. Current diverge count: '.$divergeCount.' | Persistent diverge count: '.$persistentDivergeCount,
115
                    'A call was made to SeriesProvider::maclaurinSeries() that seems to be diverging. Exiting the loop.'
116
                );
117
            }
118
119 28
            $prevDiff = $currDiff;
120 28
            $prevSum = $sum;
121
122 28
            $termNumber++;
123
        }
124
125 28
        return $sum->roundToPrecision($precision);
126
127
    }
128
129
    /**
130
     * @param callable $part1
131
     * @param callable $part2
132
     * @param callable $exponent
133
     * @param int $startTermAt
134
     * @param int $precision
135
     *
136
     * @return ImmutableDecimal
137
     */
138
    public static function genericTwoPartSeries(
139
        callable $part1,
140
        callable $part2,
141
        callable $exponent,
142
        int $startTermAt = 0,
143
        int $precision = 10): ImmutableDecimal
144
    {
145
146
        $x = Numbers::makeZero($precision+1);
147
148
        $continue = true;
149
        $termNumber = $startTermAt;
150
151
        while ($continue) {
152
            $term = Numbers::makeOne($precision+1);
153
154
            /** @var ImmutableDecimal $term */
155
            $term = $term->multiply($part2($termNumber))->pow($exponent($termNumber))
156
                ->multiply($part1($termNumber));
157
158
            if ($term->numberOfLeadingZeros()-1 >= $precision) {
159
                $continue = false;
160
            }
161
162
            $x = $x->add($term);
163
164
            $termNumber++;
165
        }
166
167
        return $x->roundToPrecision($precision+1);
168
169
    }
170
    
171
}