Completed
Pull Request — dev (#41)
by Jordan
02:45
created

SeriesProvider   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Test Coverage

Coverage 94.74%

Importance

Changes 0
Metric Value
eloc 39
dl 0
loc 115
ccs 36
cts 38
cp 0.9474
rs 10
c 0
b 0
f 0
wmc 9

2 Methods

Rating   Name   Duplication   Size   Complexity  
A genericTwoPartSeries() 0 30 3
B maclaurinSeries() 0 53 6
1
<?php
2
3
namespace Samsara\Fermat\Provider;
4
5
use Samsara\Exceptions\UsageError\IntegrityConstraint;
6
use Samsara\Fermat\Numbers;
7
use Samsara\Fermat\Types\Base\DecimalInterface;
8
use Samsara\Fermat\Types\Base\NumberInterface;
9
use Samsara\Fermat\Values\ImmutableNumber;
10
11
class SeriesProvider
12
{
13
14
    /**
15
     * Creates a series that evaluates the following:
16
     *
17
     * SUM[$startTerm -> infinity](
18
     *  $numerator($n) × $input^$exponent($n)
19
     *  --------------------------------
20
     *          $denominator($n)
21
     * )
22
     *
23
     * Where $n is the current term number, starting at $startTerm, and increasing by 1 each loop; where $numerator,
24
     * $exponent, and $denominator are callables that take the term number (as an int) as their only input, and give the
25
     * value of that section at that term number; and where $input is the x value being considered for the series.
26
     *
27
     * The function continues adding terms until a term has MORE leading zeros than the $precision setting. (That is,
28
     * until it adds zero to the total when considering significant digits.)
29
     *
30
     * @param NumberInterface       $input
31
     * @param callable              $numerator
32
     * @param callable              $exponent
33
     * @param callable              $denominator
34
     * @param int                   $startTermAt
35
     * @param int|DecimalInterface  $precision
36
     *
37
     * @return NumberInterface
38
     * @throws IntegrityConstraint
39
     */
40 19
    public static function maclaurinSeries(
41
        NumberInterface $input, // x value in series
42
        callable $numerator, // a function determining what the sign (+/-) is at the nth term
43
        callable $exponent, // a function determining the exponent of x at the nth term
44
        callable $denominator, // a function determining the denominator at the nth term
45
        $startTermAt = 0,
46
        $precision = 10)
47
    {
48
49 19
        $sum = Numbers::makeZero(100);
50 19
        $value = Numbers::make(Numbers::IMMUTABLE, $input->getValue());
51
52 19
        $continue = true;
53 19
        $termNumber = $startTermAt;
54
55 19
        $adjustmentOfZero = 0;
56
57 19
        $currentPrecision = 0;
58
59 19
        while ($continue) {
60 19
            $term = Numbers::makeOne(100);
61
62
            try {
63 19
                $term = $term->multiply($value->pow($exponent($termNumber)))
64 19
                    ->divide($denominator($termNumber), 100)
65 19
                    ->multiply($numerator($termNumber));
66
            } catch (IntegrityConstraint $constraint) {
67
                return $sum->truncateToPrecision($currentPrecision+1);
68
            }
69
70
            /** @var ImmutableNumber $term */
71 19
            if ($term->numberOfLeadingZeros() >= $precision) {
72 19
                $continue = false;
73
            }
74
75 19
            $currentPrecision = $term->numberOfLeadingZeros();
76
77 19
            if ($term->isEqual(0)) {
78 2
                $adjustmentOfZero++;
79
            } else {
80 19
                $adjustmentOfZero = 0;
81
            }
82
83 19
            if ($adjustmentOfZero > 5) {
84 2
                $continue = false;
85
            }
86
87 19
            $sum = $sum->add($term);
88
89 19
            $termNumber++;
90
        }
91
92 19
        return $sum->roundToPrecision($precision);
93
94
    }
95
96 2
    public static function genericTwoPartSeries(
97
        callable $part1,
98
        callable $part2,
99
        callable $exponent,
100
        $startTermAt = 0,
101
        $precision = 10)
102
    {
103
104 2
        $x = Numbers::makeZero($precision+1);
105
106 2
        $continue = true;
107 2
        $termNumber = $startTermAt;
108
109 2
        while ($continue) {
110 2
            $term = Numbers::makeOne($precision+1);
111
112
            /** @var ImmutableNumber $term */
113 2
            $term = $term->multiply($part2($termNumber))->pow($exponent($termNumber))
114 2
                ->multiply($part1($termNumber));
115
116 2
            if ($term->numberOfLeadingZeros()-1 >= $precision) {
117 2
                $continue = false;
118
            }
119
120 2
            $x = $x->add($term);
121
122 2
            $termNumber++;
123
        }
124
125 2
        return $x->roundToPrecision($precision+1);
126
127
    }
128
    
129
}