Passed
Push — master ( 4464ce...893faa )
by Jordan
01:38 queued 49s
created

SeriesProvider::maclaurinSeries()   B

Complexity

Conditions 10
Paths 37

Size

Total Lines 81
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 10.2714

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 45
c 1
b 0
f 0
nc 37
nop 8
dl 0
loc 81
ccs 37
cts 43
cp 0.8605
crap 10.2714
rs 7.3333

How to fix   Long Method    Complexity    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 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 $scale 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 $scale
37
     * @param int $consecutiveDivergeLimit
38
     * @param int $totalDivergeLimit
39
     *
40
     * @return ImmutableDecimal
41
     * @throws IntegrityConstraint
42
     * @throws OptionalExit
43
     * @throws ReflectionException
44
     */
45 18
    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 $scale = 10,
52
        int $consecutiveDivergeLimit = 5,
53
        int $totalDivergeLimit = 10): ImmutableDecimal
54
    {
55
56 18
        ++$scale;
57
58 18
        $sum = Numbers::makeZero($scale);
59 18
        $value = Numbers::make(Numbers::IMMUTABLE, $input->getValue(), $scale);
60
61 18
        $continue = true;
62 18
        $termNumber = $startTermAt;
63
64 18
        $adjustmentOfZero = 0;
65 18
        $prevDiff = Numbers::makeZero($scale);
66 18
        $prevSum = $sum;
67 18
        $divergeCount = -1;
68 18
        $persistentDivergeCount = -1;
69 18
        $currentScale = 0;
70
71 18
        while ($continue) {
72 18
            $term = Numbers::makeOne($scale);
73
74
            try {
75 18
                $exTerm = $value->pow($exponent($termNumber));
76 18
                $term = $term->multiply($exTerm);
77 18
                $term = $term->divide($denominator($termNumber));
78 18
                $term = $term->multiply($numerator($termNumber));
79
            } catch (IntegrityConstraint $constraint) {
80
                return $sum->truncateToScale($currentScale+1);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $sum->truncateToScale($currentScale + 1) returns the type Samsara\Fermat\Types\Bas...umbers\DecimalInterface which includes types incompatible with the type-hinted return Samsara\Fermat\Values\ImmutableDecimal.
Loading history...
81
            }
82
83
            /** @var ImmutableDecimal $term */
84 18
            if ($term->numberOfLeadingZeros() >= $scale && !$term->isWhole()) {
85 12
                $continue = false;
86
            }
87
88 18
            $currentScale = $term->numberOfLeadingZeros();
89
90 18
            if ($term->isEqual(0)) {
91 6
                $adjustmentOfZero++;
92
            } else {
93 18
                $adjustmentOfZero = 0;
94
            }
95
96 18
            if ($adjustmentOfZero > 15) {
97 6
                $continue = false;
98
            }
99
100
            /** @var ImmutableDecimal $sum */
101 18
            $sum = $sum->add($term);
102 18
            $currDiff = $sum->subtract($prevSum)->abs();
103
104 18
            if ($prevDiff->isLessThan($currDiff)) {
105 18
                $divergeCount++;
106 18
                $persistentDivergeCount++;
107
            } else {
108 18
                $divergeCount = 0;
109
            }
110
111 18
            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
                    'The series being calculated appears to be diverging, and the process has been stopped in an attempt to avoid an infinite loop.'
116
                );
117
            }
118
119 18
            $prevDiff = $currDiff;
120 18
            $prevSum = $sum;
121
122 18
            $termNumber++;
123
        }
124
125 18
        return $sum->roundToScale($scale);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $sum->roundToScale($scale) returns the type Samsara\Fermat\Types\Bas...umbers\DecimalInterface which includes types incompatible with the type-hinted return Samsara\Fermat\Values\ImmutableDecimal.
Loading history...
126
127
    }
128
    
129
}