<?php
/**
*
* Class for the management of Complex numbers
*
* @copyright Copyright (c) 2013-2018 Mark Baker (https://github.com/MarkBaker/PHPComplex)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Complex;
/**
* Complex Number object.
*
* @package Complex
*
* @method float abs()
* @method Complex acos()
* @method Complex acosh()
* @method Complex acot()
* @method Complex acoth()
* @method Complex acsc()
* @method Complex acsch()
* @method float argument()
* @method Complex asec()
* @method Complex asech()
* @method Complex asin()
* @method Complex asinh()
* @method Complex atan()
* @method Complex atanh()
* @method Complex conjugate()
* @method Complex cos()
* @method Complex cosh()
* @method Complex cot()
* @method Complex coth()
* @method Complex csc()
* @method Complex csch()
* @method Complex exp()
* @method Complex inverse()
* @method Complex ln()
* @method Complex log2()
* @method Complex log10()
* @method Complex negative()
* @method Complex pow(int|float $power)
* @method float rho()
* @method Complex sec()
* @method Complex sech()
* @method Complex sin()
* @method Complex sinh()
* @method Complex sqrt()
* @method Complex tan()
* @method Complex tanh()
* @method float theta()
* @method Complex add(...$complexValues)
* @method Complex subtract(...$complexValues)
* @method Complex multiply(...$complexValues)
* @method Complex divideby(...$complexValues)
* @method Complex divideinto(...$complexValues)
*/
class Complex
{
/**
* @constant Euler's Number.
*/
const EULER = 2.7182818284590452353602874713526624977572;
/**
* @constant Regexp to split an input string into real and imaginary components and suffix
*/
const NUMBER_SPLIT_REGEXP =
'` ^
( # Real part
[-+]?(\d+\.?\d*|\d*\.?\d+) # Real value (integer or float)
([Ee][-+]?[0-2]?\d{1,3})? # Optional real exponent for scientific format
)
( # Imaginary part
[-+]?(\d+\.?\d*|\d*\.?\d+) # Imaginary value (integer or float)
([Ee][-+]?[0-2]?\d{1,3})? # Optional imaginary exponent for scientific format
)?
( # Imaginary part is optional
([-+]?) # Imaginary (implicit 1 or -1) only
([ij]?) # Imaginary i or j - depending on whether mathematical or engineering
)
$`uix';
/**
* @var float $realPart The value of of this complex number on the real plane.
*/
protected $realPart = 0.0;
/**
* @var float $imaginaryPart The value of of this complex number on the imaginary plane.
*/
protected $imaginaryPart = 0.0;
/**
* @var string $suffix The suffix for this complex number (i or j).
*/
protected $suffix;
/**
* Validates whether the argument is a valid complex number, converting scalar or array values if possible
*
* @param mixed $complexNumber The value to parse
* @return array
* @throws Exception If the argument isn't a Complex number or cannot be converted to one
*/
private static function parseComplex($complexNumber)
{
// Test for real number, with no imaginary part
if (is_numeric($complexNumber)) {
return [$complexNumber, 0, null];
}
// Fix silly human errors
$complexNumber = str_replace(
['+-', '-+', '++', '--'],
['-', '-', '+', '+'],
$complexNumber
);
// Basic validation of string, to parse out real and imaginary parts, and any suffix
$validComplex = preg_match(
self::NUMBER_SPLIT_REGEXP,
$complexNumber,
$complexParts
);
if (!$validComplex) {
// Neither real nor imaginary part, so test to see if we actually have a suffix
$validComplex = preg_match('/^([\-\+]?)([ij])$/ui', $complexNumber, $complexParts);
if (!$validComplex) {
throw new Exception('Invalid complex number');
}
// We have a suffix, so set the real to 0, the imaginary to either 1 or -1 (as defined by the sign)
$imaginary = 1;
if ($complexParts[1] === '-') {
$imaginary = 0 - $imaginary;
}
return [0, $imaginary, $complexParts[2]];
}
// If we don't have an imaginary part, identify whether it should be +1 or -1...
if (($complexParts[4] === '') && ($complexParts[9] !== '')) {
if ($complexParts[7] !== $complexParts[9]) {
$complexParts[4] = 1;
if ($complexParts[8] === '-') {
$complexParts[4] = -1;
}
} else {
// ... or if we have only the real and no imaginary part
// (in which case our real should be the imaginary)
$complexParts[4] = $complexParts[1];
$complexParts[1] = 0;
}
}
// Return real and imaginary parts and suffix as an array, and set a default suffix if user input lazily
return [
$complexParts[1],
$complexParts[4],
!empty($complexParts[9]) ? $complexParts[9] : 'i'
];
}
public function __construct($realPart = 0.0, $imaginaryPart = null, $suffix = 'i')
{
if ($imaginaryPart === null) {
if (is_array($realPart)) {
// We have an array of (potentially) real and imaginary parts, and any suffix
list ($realPart, $imaginaryPart, $suffix) = array_values($realPart) + [0.0, 0.0, 'i'];
} elseif ((is_string($realPart)) || (is_numeric($realPart))) {
// We've been given a string to parse to extract the real and imaginary parts, and any suffix
list($realPart, $imaginaryPart, $suffix) = self::parseComplex($realPart);
}
}
< if ($imaginaryPart <> 0.0 && empty($suffix)) {
>
> if ($imaginaryPart != 0.0 && empty($suffix)) {
$suffix = 'i';
> } elseif ($imaginaryPart == 0.0 && !empty($suffix)) {
}
> $suffix = '';
// Set parsed values in our properties
$this->realPart = (float) $realPart;
$this->imaginaryPart = (float) $imaginaryPart;
< $this->suffix = strtolower($suffix);
> $this->suffix = strtolower($suffix ?? '');
}
/**
* Gets the real part of this complex number
*
* @return Float
*/
< public function getReal()
> public function getReal(): float
{
return $this->realPart;
}
/**
* Gets the imaginary part of this complex number
*
* @return Float
*/
< public function getImaginary()
> public function getImaginary(): float
{
return $this->imaginaryPart;
}
/**
* Gets the suffix of this complex number
*
* @return String
*/
< public function getSuffix()
> public function getSuffix(): string
{
return $this->suffix;
}
/**
* Returns true if this is a real value, false if a complex value
*
* @return Bool
*/
< public function isReal()
> public function isReal(): bool
{
return $this->imaginaryPart == 0.0;
}
/**
* Returns true if this is a complex value, false if a real value
*
* @return Bool
*/
< public function isComplex()
> public function isComplex(): bool
{
return !$this->isReal();
}
< public function format()
> public function format(): string
{
$str = "";
if ($this->imaginaryPart != 0.0) {
if (\abs($this->imaginaryPart) != 1.0) {
$str .= $this->imaginaryPart . $this->suffix;
} else {
$str .= (($this->imaginaryPart < 0.0) ? '-' : '') . $this->suffix;
}
}
if ($this->realPart != 0.0) {
if (($str) && ($this->imaginaryPart > 0.0)) {
$str = "+" . $str;
}
$str = $this->realPart . $str;
}
if (!$str) {
$str = "0.0";
}
return $str;
}
< public function __toString()
> public function __toString(): string
{
return $this->format();
}
/**
* Validates whether the argument is a valid complex number, converting scalar or array values if possible
*
* @param mixed $complex The value to validate
* @return Complex
* @throws Exception If the argument isn't a Complex number or cannot be converted to one
*/
< public static function validateComplexArgument($complex)
> public static function validateComplexArgument($complex): Complex
{
if (is_scalar($complex) || is_array($complex)) {
$complex = new Complex($complex);
} elseif (!is_object($complex) || !($complex instanceof Complex)) {
throw new Exception('Value is not a valid complex number');
}
return $complex;
}
/**
* Returns the reverse of this complex number
*
* @return Complex
*/
< public function reverse()
> public function reverse(): Complex
{
return new Complex(
$this->imaginaryPart,
$this->realPart,
($this->realPart == 0.0) ? null : $this->suffix
);
}
< public function invertImaginary()
> public function invertImaginary(): Complex
{
return new Complex(
$this->realPart,
$this->imaginaryPart * -1,
($this->imaginaryPart == 0.0) ? null : $this->suffix
);
}
< public function invertReal()
> public function invertReal(): Complex
{
return new Complex(
$this->realPart * -1,
$this->imaginaryPart,
($this->imaginaryPart == 0.0) ? null : $this->suffix
);
}
protected static $functions = [
'abs',
'acos',
'acosh',
'acot',
'acoth',
'acsc',
'acsch',
'argument',
'asec',
'asech',
'asin',
'asinh',
'atan',
'atanh',
'conjugate',
'cos',
'cosh',
'cot',
'coth',
'csc',
'csch',
'exp',
'inverse',
'ln',
'log2',
'log10',
'negative',
'pow',
'rho',
'sec',
'sech',
'sin',
'sinh',
'sqrt',
'tan',
'tanh',
'theta',
];
protected static $operations = [
'add',
'subtract',
'multiply',
'divideby',
'divideinto',
];
/**
* Returns the result of the function call or operation
*
* @return Complex|float
* @throws Exception|\InvalidArgumentException
*/
public function __call($functionName, $arguments)
{
$functionName = strtolower(str_replace('_', '', $functionName));
// Test for function calls
< if (in_array($functionName, self::$functions)) {
< $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}";
< return $functionName($this, ...$arguments);
> if (in_array($functionName, self::$functions, true)) {
> return Functions::$functionName($this, ...$arguments);
}
// Test for operation calls
< if (in_array($functionName, self::$operations)) {
< $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}";
< return $functionName($this, ...$arguments);
> if (in_array($functionName, self::$operations, true)) {
> return Operations::$functionName($this, ...$arguments);
}
< throw new Exception('Function or Operation does not exist');
> throw new Exception('Complex Function or Operation does not exist');
}
}