123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- <?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)) {
- $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);
- }
- /**
- * Gets the real part of this complex number
- *
- * @return Float
- */
- public function getReal()
- {
- return $this->realPart;
- }
- /**
- * Gets the imaginary part of this complex number
- *
- * @return Float
- */
- public function getImaginary()
- {
- return $this->imaginaryPart;
- }
- /**
- * Gets the suffix of this complex number
- *
- * @return String
- */
- public function getSuffix()
- {
- return $this->suffix;
- }
- /**
- * Returns true if this is a real value, false if a complex value
- *
- * @return Bool
- */
- public function isReal()
- {
- return $this->imaginaryPart == 0.0;
- }
- /**
- * Returns true if this is a complex value, false if a real value
- *
- * @return Bool
- */
- public function isComplex()
- {
- return !$this->isReal();
- }
- public function format()
- {
- $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()
- {
- 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)
- {
- 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()
- {
- return new Complex(
- $this->imaginaryPart,
- $this->realPart,
- ($this->realPart == 0.0) ? null : $this->suffix
- );
- }
- public function invertImaginary()
- {
- return new Complex(
- $this->realPart,
- $this->imaginaryPart * -1,
- ($this->imaginaryPart == 0.0) ? null : $this->suffix
- );
- }
- public function invertReal()
- {
- 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, true)) {
- $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}";
- return $functionName($this, ...$arguments);
- }
- // Test for operation calls
- if (in_array($functionName, self::$operations, true)) {
- $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}";
- return $functionName($this, ...$arguments);
- }
- throw new Exception('Function or Operation does not exist');
- }
- }
|