Completed
Pull Request — dev (#33)
by Jordan
01:55
created

SeriesProvider::genericTwoPartSeries()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 30
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 3
nop 5
dl 0
loc 30
ccs 13
cts 13
cp 1
crap 3
rs 9.8666
c 0
b 0
f 0
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
     */
39 12
    public static function maclaurinSeries(
40
        NumberInterface $input, // x value in series
41
        callable $numerator, // a function determining what the sign (+/-) at the nth term
42
        callable $exponent, // a function determining the exponent of x at the nth term
43
        callable $denominator, // a function determining the denominator at the nth term
44
        $startTermAt = 0,
45
        $precision = 10)
46
    {
47
48 12
        $x = Numbers::makeZero(100);
49 12
        $value = Numbers::make(Numbers::IMMUTABLE, $input->getValue());
50
51 12
        $continue = true;
52 12
        $termNumber = $startTermAt;
53
54 12
        $adjustmentOfZero = 0;
55
56 12
        $currentPrecision = 0;
57
58 12
        while ($continue) {
59 12
            $term = Numbers::makeOne(100);
60
61
            try {
62 12
                $term = $term->multiply($value->pow($exponent($termNumber)))
63 12
                    ->divide($denominator($termNumber), 100)
64 12
                    ->multiply($numerator($termNumber));
65
            } catch (IntegrityConstraint $constraint) {
66
                return $x->roundToPrecision($currentPrecision+1);
67
            }
68
69 12
            if ($term->numberOfLeadingZeros() >= $precision) {
70 12
                $continue = false;
71
            }
72
73 12
            $currentPrecision = $term->numberOfLeadingZeros();
74
75 12
            if ($term->isEqual(0)) {
76
                $adjustmentOfZero++;
77
            } else {
78 12
                $adjustmentOfZero = 0;
79
            }
80
81 12
            if ($adjustmentOfZero > 5) {
82
                $continue = false;
83
            }
84
85 12
            $x = $x->add($term);
86
87 12
            $termNumber++;
88
        }
89
90 12
        return $x->roundToPrecision($precision);
91
92
    }
93
94 2
    public static function genericTwoPartSeries(
95
        callable $part1,
96
        callable $part2,
97
        callable $exponent,
98
        $startTermAt = 0,
99
        $precision = 10)
100
    {
101
102 2
        $x = Numbers::makeZero($precision+1);
103
104 2
        $continue = true;
105 2
        $termNumber = $startTermAt;
106
107 2
        while ($continue) {
108 2
            $term = Numbers::makeOne($precision+1);
109
110
            /** @var ImmutableNumber $term */
111 2
            $term = $term->multiply($part2($termNumber))->pow($exponent($termNumber))
112 2
                ->multiply($part1($termNumber));
113
114 2
            if ($term->numberOfLeadingZeros()-1 >= $precision) {
115 2
                $continue = false;
116
            }
117
118 2
            $x = $x->add($term);
119
120 2
            $termNumber++;
121
        }
122
123 2
        return $x->roundToPrecision($precision+1);
124
125
    }
126
    
127
}