Failed Conditions
Pull Request — master (#47)
by Jordan
07:14 queued 03:12
created

PolynomialFunction::createFromFoil()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 48
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 26
nc 8
nop 2
dl 0
loc 48
ccs 0
cts 25
cp 0
crap 30
rs 9.1928
c 1
b 0
f 0
1
<?php
2
3
namespace Samsara\Fermat\Values\Algebra;
4
5
use Samsara\Exceptions\UsageError\IntegrityConstraint;
6
use Samsara\Fermat\Numbers;
7
use Samsara\Fermat\Types\Base\Interfaces\Numbers\DecimalInterface;
8
use Samsara\Fermat\Types\Base\Interfaces\Evaluateables\FunctionInterface;
9
use Samsara\Fermat\Types\Base\Interfaces\Numbers\NumberInterface;
10
use Samsara\Fermat\Types\Expression;
11
use Samsara\Fermat\Values\ImmutableDecimal;
12
13
class PolynomialFunction extends Expression implements FunctionInterface
14
{
15
    /** @var array  */
16
    protected $coefficients = [];
17
18
    /**
19
     * PolynomialFunction constructor.
20
     *
21
     * @param array $coefficients
22
     *
23
     * @throws IntegrityConstraint
24
     */
25 4
    public function __construct(array $coefficients)
26
    {
27 4
        parent::__construct(Expression::POLYNOMIAL);
28
29 4
        $sanitizedCoefficients = [];
30
31 4
        foreach ($coefficients as $exponent => $coefficient) {
32 4
            if (!is_int($exponent)) {
33 1
                throw new IntegrityConstraint(
34 1
                    'Keys in the $coefficients array must be integers',
35 1
                    'Only use integer keys for the $coefficients array',
36 1
                    'The key '.$exponent.' was found in the $coefficients array; an integer was expected'
37
                );
38
            }
39
40
            /** @var ImmutableDecimal $fermatCoefficient */
41 3
            $fermatCoefficient = Numbers::make(Numbers::IMMUTABLE, $coefficient);
42
43 3
            if (!$fermatCoefficient->isEqual(0)) {
44 3
                $sanitizedCoefficients[$exponent] = $fermatCoefficient;
45
            }
46
        }
47
48 3
        $this->coefficients = $sanitizedCoefficients;
49
50
        $this->expression = function($x): ImmutableDecimal {
51
            /** @var ImmutableDecimal $value */
52 3
            $value = Numbers::makeZero();
53
54
            /** @var ImmutableDecimal $xPart */
55 3
            $xPart = Numbers::makeOrDont(Numbers::IMMUTABLE, $x);
56
57 3
            foreach ($this->coefficients as $exponent => $coefficient) {
58 3
                if ($exponent == 0) {
59 3
                    $value = $value->add($coefficient);
60
                } else {
61 3
                    $term = $coefficient->multiply($xPart->pow($exponent));
62 3
                    $value = $value->add($term);
63
                }
64
            }
65
66 3
            if ($value->isEqual(0)) {
67
                $value = Numbers::makeOne();
68
            }
69
70 3
            return $value;
71
        };
72 3
    }
73
74
    /**
75
     * @param int|float|string|NumberInterface|DecimalInterface $x
76
     *
77
     * @return ImmutableDecimal
78
     */
79 3
    public function evaluateAt($x): ImmutableDecimal
80
    {
81
        /** @var callable $answer */
82 3
        $answer = $this->expression;
83
84 3
        return $answer($x);
85
    }
86
87
    /**
88
     * @return FunctionInterface
89
     * @throws IntegrityConstraint
90
     */
91 1
    public function derivativeExpression(): FunctionInterface
92
    {
93 1
        $newCoefficients = [];
94
95
        /**
96
         * @var int             $exponent
97
         * @var ImmutableDecimal $coefficient
98
         */
99 1
        foreach ($this->coefficients as $exponent => $coefficient) {
100 1
            if ($exponent == 0) {
101 1
                continue;
102
            }
103
104 1
            $newCoefficients[$exponent-1] = $coefficient->multiply($exponent);
105
        }
106
107 1
        return new PolynomialFunction($newCoefficients);
108
    }
109
110
    /**
111
     * @param int|float|string|NumberInterface|DecimalInterface $C
112
     *
113
     * @return FunctionInterface
114
     * @throws IntegrityConstraint
115
     */
116 1
    public function integralExpression($C = 0): FunctionInterface
117
    {
118 1
        $C = Numbers::make(Numbers::IMMUTABLE, $C);
119
120 1
        $newCoefficients = [];
121
122 1
        if (!$C->isEqual(0)) {
123 1
            $newCoefficients[0] = $C;
124
        }
125
126
        /**
127
         * @var int             $exponent
128
         * @var ImmutableDecimal $coefficient
129
         */
130 1
        foreach ($this->coefficients as $exponent => $coefficient) {
131 1
            $newExponent = $exponent+1;
132
133 1
            $newCoefficients[$newExponent] = $coefficient->divide($newExponent);
134
        }
135
136 1
        return new PolynomialFunction($newCoefficients);
137
    }
138
139 3
    public function describeShape(): array
140
    {
141
142 3
        $shape = [];
143
144
        /**
145
         * @var int             $exponent
146
         * @var ImmutableDecimal $coefficient
147
         */
148 3
        foreach ($this->coefficients as $exponent => $coefficient) {
149 3
            $shape[$exponent] = $coefficient->getValue();
150
        }
151
152 3
        return $shape;
153
154
    }
155
156
    /**
157
     * This function performs a FOIL expansion on a list of parameters.
158
     *
159
     * Assumptions:
160
     *      1. The coefficients are the numbers provided in the arrays
161
     *      2. The coefficients are listed in descending order of their exponent on the function variable. For example,
162
     *         if you were multiplying (2 + 3x)*(5 - 1x^2 + 1x), it would expect these inputs:
163
     *              -  [3, 2], [-1, 1, 5]
164
     *      3. If not all exponents are used continuously, a zero must be provided for the position that is skipped. For
165
     *         example, if one of the provided groups was 4x^2 + 2, it would expect: [4, 0, 2]
166
     *
167
     * @param int[]|float[]|NumberInterface[] $group1
168
     * @param int[]|float[]|NumberInterface[] $group2
169
     *
170
     * @return PolynomialFunction
171
     * @throws IntegrityConstraint
172
     */
173
    public static function createFromFoil(array $group1, array $group2): PolynomialFunction
174
    {
175
        $group1exp = count($group1)-1;
176
        $group2exp = count($group2)-1;
177
178
        /** @var ImmutableDecimal[] $finalCoefs */
179
        $finalCoefs = [];
180
181
        $group1 = Numbers::make(Numbers::IMMUTABLE, $group1);
182
        $group2 = Numbers::make(Numbers::IMMUTABLE, $group2);
183
184
        if ($group1exp <= $group2exp) {
185
            $largerGroup = $group2;
186
            $largerExp = $group2exp;
187
            $smallerGroup = $group1;
188
            $smallerExp = $group1exp;
189
        } else {
190
            $largerGroup = $group1;
191
            $largerExp = $group1exp;
192
            $smallerGroup = $group2;
193
            $smallerExp = $group2exp;
194
        }
195
196
        /**
197
         * @var int $key1
198
         * @var ImmutableDecimal $value1
199
         */
200
        foreach ($largerGroup as $key1 => $value1) {
201
            $largerKey = $largerExp - $key1;
202
203
            /**
204
             * @var int             $key2
205
             * @var ImmutableDecimal $value2
206
             */
207
            foreach ($smallerGroup as $key2 => $value2) {
208
                $smallerKey = $smallerExp - $key2;
209
                $newVal = $value1->multiply($value2);
210
                $newExp = $largerKey + $smallerKey;
211
212
                if (isset($finalCoefs[$newExp])) {
213
                    $finalCoefs[$newExp] = $finalCoefs[$newExp]->add($newVal);
214
                } else {
215
                    $finalCoefs[$newExp] = $newVal;
216
                }
217
            }
218
        }
219
220
        return new PolynomialFunction($finalCoefs);
221
    }
222
223
}