ComplexNumber::getAsBaseTenRealNumber()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
3
namespace Samsara\Fermat\Complex\Types;
4
5
use Samsara\Exceptions\SystemError\LogicalError\IncompatibleObjectState;
6
use Samsara\Exceptions\UsageError\IntegrityConstraint;
7
use Samsara\Exceptions\UsageError\OptionalExit;
8
use Samsara\Fermat\Complex\ComplexNumbers;
9
use Samsara\Fermat\Complex\Types\Traits\ComplexScaleTrait;
0 ignored issues
show
Bug introduced by
The type Samsara\Fermat\Complex\T...raits\ComplexScaleTrait was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Samsara\Fermat\Complex\Values\MutableComplexNumber;
0 ignored issues
show
Bug introduced by
The type Samsara\Fermat\Complex\Values\MutableComplexNumber was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use Samsara\Fermat\Coordinates\Values\PolarCoordinate;
12
use Samsara\Fermat\Core\Enums\CalcMode;
13
use Samsara\Fermat\Core\Enums\NumberBase;
14
use Samsara\Fermat\Core\Enums\RoundingMode;
15
use Samsara\Fermat\Core\Numbers;
16
use Samsara\Fermat\Complex\Types\Base\Interfaces\Numbers\ComplexNumberInterface;
17
use Samsara\Fermat\Core\Types\Base\Interfaces\Numbers\NumberInterface;
0 ignored issues
show
Bug introduced by
The type Samsara\Fermat\Core\Type...Numbers\NumberInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Samsara\Fermat\Core\Types\Base\Interfaces\Numbers\ScaleInterface;
0 ignored issues
show
Bug introduced by
The type Samsara\Fermat\Core\Type...\Numbers\ScaleInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use Samsara\Fermat\Core\Types\Base\Interfaces\Numbers\SimpleNumberInterface;
20
use Samsara\Fermat\Complex\Types\Traits\ArithmeticComplexTrait;
0 ignored issues
show
Bug introduced by
The type Samsara\Fermat\Complex\T...\ArithmeticComplexTrait was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use Samsara\Fermat\Coordinates\Values\CartesianCoordinate;
22
use Samsara\Fermat\Complex\Values\ImmutableComplexNumber;
23
use Samsara\Fermat\Core\Types\Base\Number;
24
use Samsara\Fermat\Core\Types\Decimal;
0 ignored issues
show
Bug introduced by
The type Samsara\Fermat\Core\Types\Decimal was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use Samsara\Fermat\Core\Types\Traits\CalculationModeTrait;
26
use Samsara\Fermat\Core\Values\ImmutableFraction;
27
use Samsara\Fermat\Core\Values\ImmutableDecimal;
28
use Samsara\Fermat\Core\Types\Fraction;
0 ignored issues
show
Bug introduced by
The type Samsara\Fermat\Core\Types\Fraction was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
30
/**
31
 *
32
 */
33
abstract class ComplexNumber extends Number implements ComplexNumberInterface, ScaleInterface
34
{
35
36
    protected ImmutableDecimal|ImmutableFraction $realPart;
37
    protected ImmutableDecimal|ImmutableFraction $imaginaryPart;
38
    protected CartesianCoordinate $cachedCartesian;
39
    protected PolarCoordinate $cachedPolar;
40
41
42
    use ArithmeticComplexTrait;
43
    use CalculationModeTrait;
44
    use ComplexScaleTrait;
45
46
    /**
47
     * @param ImmutableDecimal|ImmutableFraction $realPart
48
     * @param ImmutableDecimal|ImmutableFraction $imaginaryPart
49
     * @param int|null $scale
50
     * @param NumberBase $base
51
     * @throws IncompatibleObjectState
52
     * @throws IntegrityConstraint
53
     * @throws OptionalExit
54
     */
55 268
    public function __construct(
56
        ImmutableDecimal|ImmutableFraction $realPart,
57
        ImmutableDecimal|ImmutableFraction $imaginaryPart,
58
        ?int $scale = null,
59
        NumberBase $base = NumberBase::Ten
60
    )
61
    {
62 268
        $partsScale = ($realPart->getScale() > $imaginaryPart->getScale()) ? $realPart->getScale() : $imaginaryPart->getScale();
63 268
        $scale = $scale ?? $partsScale;
64 268
        $this->scale = $scale;
65
66 268
        $this->realPart = $realPart->roundToScale($scale)->setBase($base);
67 268
        $this->imaginaryPart = $imaginaryPart->roundToScale($scale)->setBase($base);
68
69 268
        $cartesian = new CartesianCoordinate(
70 268
            $this->realPart,
71 268
            Numbers::make(
72
                Numbers::IMMUTABLE,
73 268
                $this->imaginaryPart->getAsBaseTenRealNumber()
74
            )
75
        );
76
77 268
        $this->cachedCartesian = $cartesian;
78 268
        $this->cachedPolar = $cartesian->asPolar($this->scale + $this->realPart->numberOfTotalDigits() + $this->imaginaryPart->numberOfTotalDigits());
79
80 268
        parent::__construct();
81
    }
82
83
    /**
84
     * Allows you to set a mode on a number to select the calculation methods.
85
     *
86
     * @param ?CalcMode $mode
87
     * @return $this
88
     */
89 266
    public function setMode(?CalcMode $mode): self
90
    {
91 266
        $this->calcMode = $mode;
92
93 266
        $this->realPart->setMode($mode);
94 266
        $this->imaginaryPart->setMode($mode);
95
96 266
        return $this;
97
    }
98
99
    /**
100
     * @return PolarCoordinate
101
     */
102
    public function asPolar(): PolarCoordinate
103
    {
104
        return $this->cachedPolar;
105
    }
106
107
    /**
108
     * @return string
109
     */
110
    public function getAsBaseTenRealNumber(): string
111
    {
112
        return $this->getDistanceFromOrigin();
113
    }
114
115
    /**
116
     * @return ImmutableDecimal|ImmutableFraction
117
     */
118 268
    public function getRealPart(): ImmutableDecimal|ImmutableFraction
119
    {
120 268
        return $this->realPart;
121
    }
122
123
    /**
124
     * @return ImmutableDecimal|ImmutableFraction
125
     */
126 268
    public function getImaginaryPart(): ImmutableDecimal|ImmutableFraction
127
    {
128 268
        return $this->imaginaryPart;
129
    }
130
131
    /**
132
     * @return bool
133
     */
134 242
    public function isComplex(): bool
135
    {
136 242
        return true;
137
    }
138
139
    /**
140
     * @return bool
141
     */
142
    public function isImaginary(): bool
143
    {
144
        return false;
145
    }
146
147
    /**
148
     * @return bool
149
     */
150
    public function isReal(): bool
151
    {
152
        return false;
153
    }
154
155
    /**
156
     * @return ImmutableDecimal|ImmutableFraction
157
     */
158
    public function asReal(): ImmutableDecimal|ImmutableFraction
159
    {
160
        return (new ImmutableDecimal($this->getAsBaseTenRealNumber(), $this->getScale()))->setMode($this->getMode());
161
    }
162
163
    /**
164
     * @param string|int|float|Decimal|Fraction|ComplexNumber $value
165
     *
166
     * @return bool
167
     */
168 4
    public function isEqual(string|int|float|Decimal|Fraction|ComplexNumber $value): bool
169
    {
170 4
        if (is_int($value) || is_float($value)) {
171 4
            return false;
172
        }
173
174
        if (is_string($value) && !str_contains($value, 'i')) {
175
            return false;
176
        } else {
177
            $value = ComplexNumbers::make(ComplexNumbers::IMMUTABLE_COMPLEX, $value);
178
        }
179
180
        if (!$value->isComplex()) {
181
            return false;
182
        }
183
184
        if (!($value instanceof NumberInterface)) {
185
            if (is_string($value)) {
0 ignored issues
show
introduced by
The condition is_string($value) is always false.
Loading history...
186
                try {
187
                    $value = static::makeFromString($value);
188
                } catch (IntegrityConstraint) {
189
                    return false;
190
                }
191
            } elseif (is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
192
                try {
193
                    $value = static::makeFromArray($value);
194
                } catch (IntegrityConstraint) {
195
                    return false;
196
                }
197
            } else {
198
                return false;
199
            }
200
        }
201
202
        return $this->getValue() === $value->getValue();
203
    }
204
205
    /**
206
     * @param array $number
207
     * @param $scale
208
     * @param NumberBase $base
209
     * @return ComplexNumber
210
     * @throws IncompatibleObjectState
211
     * @throws IntegrityConstraint
212
     * @throws OptionalExit
213
     */
214
    public static function makeFromArray(array $number, $scale = null, NumberBase $base = NumberBase::Ten): ComplexNumber
215
    {
216
217
        if (count($number) != 2) {
218
            throw new IntegrityConstraint(
219
                'Exactly two numbers must be provided for a ComplexNumber',
220
                'Provide two numbers in the array.',
221
                'Attempt made to create ComplexNumber with incorrect amount of input numbers.'
222
            );
223
        }
224
225
        [$part1, $part2] = $number;
226
227
        $part1 = Numbers::make(Numbers::IMMUTABLE, $part1);
228
        $part2 = Numbers::make(Numbers::IMMUTABLE, $part2);
229
230
        if (($part1->isReal() && $part2->isReal()) || ($part1->isImaginary() && $part2->isImaginary())) {
231
            throw new IntegrityConstraint(
232
                'A complex number must have both an imaginary component and real component.',
233
                'Provide a string that contains both an imaginary number and a real number.',
234
                'Attempted to make a complex number from two numbers of the same type.'
235
            );
236
        }
237
238
        $realPart = $part1->isReal() ? $part1 : $part2;
239
        $imaginaryPart = $part2->isImaginary() ? $part2 : $part1;
240
241
        return new static($realPart, $imaginaryPart, $scale, $base);
242
243
    }
244
245
    /**
246
     * @param string $expression
247
     * @param $scale
248
     * @param NumberBase $base
249
     * @return ComplexNumber
250
     * @throws IncompatibleObjectState
251
     * @throws IntegrityConstraint
252
     * @throws OptionalExit
253
     */
254
    public static function makeFromString(string $expression, $scale = null, NumberBase $base = NumberBase::Ten): ComplexNumber
255
    {
256
        if (str_contains($expression, '+')) {
257
            [$part1, $part2] = explode('+', $expression);
258
        } elseif (str_contains($expression, '-')) {
259
            [$part1, $part2] = explode('-', $expression);
260
        } else {
261
            throw new IntegrityConstraint(
262
                'To make a complex number from a string, it must have both a real part and a complex part.',
263
                'Provide a string in a format that can be read, such as "1 + 2i", "2 - 1i", or "6i - 5".',
264
                'Cannot determine real part and imaginary part of complex number from given string.'
265
            );
266
        }
267
268
        return static::makeFromArray([$part1, $part2], $scale, $base);
269
    }
270
271
    /**
272
     * @return ImmutableDecimal
273
     */
274 2
    public function getDistanceFromOrigin(): ImmutableDecimal
275
    {
276 2
        return $this->cachedPolar->getDistanceFromOrigin();
277
    }
278
279
    /**
280
     * @return ImmutableDecimal
281
     */
282
    public function getPolarAngle(): ImmutableDecimal
283
    {
284
        return $this->cachedPolar->getPolarAngle();
285
    }
286
287
    /**
288
     * @return ImmutableDecimal
289
     */
290 2
    public function abs(): ImmutableDecimal
291
    {
292 2
        return $this->getDistanceFromOrigin()->roundToScale($this->getScale());
293
    }
294
295
    /**
296
     * @return string
297
     */
298 2
    public function absValue(): string
299
    {
300 2
        return $this->abs()->getValue();
301
    }
302
303
    /**
304
     * @param NumberBase $base
305
     * @return string
306
     * @throws IntegrityConstraint
307
     */
308 202
    public function getValue(NumberBase $base = NumberBase::Ten): string
309
    {
310 202
        if (!$this->getImaginaryPart()->isNegative()) {
311 138
            $joiner = '+';
312
        } else {
313 68
            $joiner = '';
314
        }
315
316 202
        return $this->getRealPart()->getValue($base).$joiner.$this->getImaginaryPart()->getValue($base);
317
    }
318
319
    /**
320
     * @return ImmutableComplexNumber
321
     */
322
    public function asComplex(): ImmutableComplexNumber
323
    {
324
        return new ImmutableComplexNumber($this->getRealPart(), $this->getImaginaryPart());
325
    }
326
327
}