PageRenderTime 58ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Complex.php

https://github.com/mackensen/moodle
PHP | 388 lines | 220 code | 34 blank | 134 comment | 36 complexity | 64ff6379f814d52dfd3a9f6072821492 MD5 | raw file
  1. <?php
  2. /**
  3. *
  4. * Class for the management of Complex numbers
  5. *
  6. * @copyright Copyright (c) 2013-2018 Mark Baker (https://github.com/MarkBaker/PHPComplex)
  7. * @license https://opensource.org/licenses/MIT MIT
  8. */
  9. namespace Complex;
  10. /**
  11. * Complex Number object.
  12. *
  13. * @package Complex
  14. *
  15. * @method float abs()
  16. * @method Complex acos()
  17. * @method Complex acosh()
  18. * @method Complex acot()
  19. * @method Complex acoth()
  20. * @method Complex acsc()
  21. * @method Complex acsch()
  22. * @method float argument()
  23. * @method Complex asec()
  24. * @method Complex asech()
  25. * @method Complex asin()
  26. * @method Complex asinh()
  27. * @method Complex atan()
  28. * @method Complex atanh()
  29. * @method Complex conjugate()
  30. * @method Complex cos()
  31. * @method Complex cosh()
  32. * @method Complex cot()
  33. * @method Complex coth()
  34. * @method Complex csc()
  35. * @method Complex csch()
  36. * @method Complex exp()
  37. * @method Complex inverse()
  38. * @method Complex ln()
  39. * @method Complex log2()
  40. * @method Complex log10()
  41. * @method Complex negative()
  42. * @method Complex pow(int|float $power)
  43. * @method float rho()
  44. * @method Complex sec()
  45. * @method Complex sech()
  46. * @method Complex sin()
  47. * @method Complex sinh()
  48. * @method Complex sqrt()
  49. * @method Complex tan()
  50. * @method Complex tanh()
  51. * @method float theta()
  52. * @method Complex add(...$complexValues)
  53. * @method Complex subtract(...$complexValues)
  54. * @method Complex multiply(...$complexValues)
  55. * @method Complex divideby(...$complexValues)
  56. * @method Complex divideinto(...$complexValues)
  57. */
  58. class Complex
  59. {
  60. /**
  61. * @constant Euler's Number.
  62. */
  63. const EULER = 2.7182818284590452353602874713526624977572;
  64. /**
  65. * @constant Regexp to split an input string into real and imaginary components and suffix
  66. */
  67. const NUMBER_SPLIT_REGEXP =
  68. '` ^
  69. ( # Real part
  70. [-+]?(\d+\.?\d*|\d*\.?\d+) # Real value (integer or float)
  71. ([Ee][-+]?[0-2]?\d{1,3})? # Optional real exponent for scientific format
  72. )
  73. ( # Imaginary part
  74. [-+]?(\d+\.?\d*|\d*\.?\d+) # Imaginary value (integer or float)
  75. ([Ee][-+]?[0-2]?\d{1,3})? # Optional imaginary exponent for scientific format
  76. )?
  77. ( # Imaginary part is optional
  78. ([-+]?) # Imaginary (implicit 1 or -1) only
  79. ([ij]?) # Imaginary i or j - depending on whether mathematical or engineering
  80. )
  81. $`uix';
  82. /**
  83. * @var float $realPart The value of of this complex number on the real plane.
  84. */
  85. protected $realPart = 0.0;
  86. /**
  87. * @var float $imaginaryPart The value of of this complex number on the imaginary plane.
  88. */
  89. protected $imaginaryPart = 0.0;
  90. /**
  91. * @var string $suffix The suffix for this complex number (i or j).
  92. */
  93. protected $suffix;
  94. /**
  95. * Validates whether the argument is a valid complex number, converting scalar or array values if possible
  96. *
  97. * @param mixed $complexNumber The value to parse
  98. * @return array
  99. * @throws Exception If the argument isn't a Complex number or cannot be converted to one
  100. */
  101. private static function parseComplex($complexNumber)
  102. {
  103. // Test for real number, with no imaginary part
  104. if (is_numeric($complexNumber)) {
  105. return [$complexNumber, 0, null];
  106. }
  107. // Fix silly human errors
  108. $complexNumber = str_replace(
  109. ['+-', '-+', '++', '--'],
  110. ['-', '-', '+', '+'],
  111. $complexNumber
  112. );
  113. // Basic validation of string, to parse out real and imaginary parts, and any suffix
  114. $validComplex = preg_match(
  115. self::NUMBER_SPLIT_REGEXP,
  116. $complexNumber,
  117. $complexParts
  118. );
  119. if (!$validComplex) {
  120. // Neither real nor imaginary part, so test to see if we actually have a suffix
  121. $validComplex = preg_match('/^([\-\+]?)([ij])$/ui', $complexNumber, $complexParts);
  122. if (!$validComplex) {
  123. throw new Exception('Invalid complex number');
  124. }
  125. // We have a suffix, so set the real to 0, the imaginary to either 1 or -1 (as defined by the sign)
  126. $imaginary = 1;
  127. if ($complexParts[1] === '-') {
  128. $imaginary = 0 - $imaginary;
  129. }
  130. return [0, $imaginary, $complexParts[2]];
  131. }
  132. // If we don't have an imaginary part, identify whether it should be +1 or -1...
  133. if (($complexParts[4] === '') && ($complexParts[9] !== '')) {
  134. if ($complexParts[7] !== $complexParts[9]) {
  135. $complexParts[4] = 1;
  136. if ($complexParts[8] === '-') {
  137. $complexParts[4] = -1;
  138. }
  139. } else {
  140. // ... or if we have only the real and no imaginary part
  141. // (in which case our real should be the imaginary)
  142. $complexParts[4] = $complexParts[1];
  143. $complexParts[1] = 0;
  144. }
  145. }
  146. // Return real and imaginary parts and suffix as an array, and set a default suffix if user input lazily
  147. return [
  148. $complexParts[1],
  149. $complexParts[4],
  150. !empty($complexParts[9]) ? $complexParts[9] : 'i'
  151. ];
  152. }
  153. public function __construct($realPart = 0.0, $imaginaryPart = null, $suffix = 'i')
  154. {
  155. if ($imaginaryPart === null) {
  156. if (is_array($realPart)) {
  157. // We have an array of (potentially) real and imaginary parts, and any suffix
  158. list ($realPart, $imaginaryPart, $suffix) = array_values($realPart) + [0.0, 0.0, 'i'];
  159. } elseif ((is_string($realPart)) || (is_numeric($realPart))) {
  160. // We've been given a string to parse to extract the real and imaginary parts, and any suffix
  161. list($realPart, $imaginaryPart, $suffix) = self::parseComplex($realPart);
  162. }
  163. }
  164. if ($imaginaryPart != 0.0 && empty($suffix)) {
  165. $suffix = 'i';
  166. } elseif ($imaginaryPart == 0.0 && !empty($suffix)) {
  167. $suffix = '';
  168. }
  169. // Set parsed values in our properties
  170. $this->realPart = (float) $realPart;
  171. $this->imaginaryPart = (float) $imaginaryPart;
  172. $this->suffix = strtolower($suffix ?? '');
  173. }
  174. /**
  175. * Gets the real part of this complex number
  176. *
  177. * @return Float
  178. */
  179. public function getReal(): float
  180. {
  181. return $this->realPart;
  182. }
  183. /**
  184. * Gets the imaginary part of this complex number
  185. *
  186. * @return Float
  187. */
  188. public function getImaginary(): float
  189. {
  190. return $this->imaginaryPart;
  191. }
  192. /**
  193. * Gets the suffix of this complex number
  194. *
  195. * @return String
  196. */
  197. public function getSuffix(): string
  198. {
  199. return $this->suffix;
  200. }
  201. /**
  202. * Returns true if this is a real value, false if a complex value
  203. *
  204. * @return Bool
  205. */
  206. public function isReal(): bool
  207. {
  208. return $this->imaginaryPart == 0.0;
  209. }
  210. /**
  211. * Returns true if this is a complex value, false if a real value
  212. *
  213. * @return Bool
  214. */
  215. public function isComplex(): bool
  216. {
  217. return !$this->isReal();
  218. }
  219. public function format(): string
  220. {
  221. $str = "";
  222. if ($this->imaginaryPart != 0.0) {
  223. if (\abs($this->imaginaryPart) != 1.0) {
  224. $str .= $this->imaginaryPart . $this->suffix;
  225. } else {
  226. $str .= (($this->imaginaryPart < 0.0) ? '-' : '') . $this->suffix;
  227. }
  228. }
  229. if ($this->realPart != 0.0) {
  230. if (($str) && ($this->imaginaryPart > 0.0)) {
  231. $str = "+" . $str;
  232. }
  233. $str = $this->realPart . $str;
  234. }
  235. if (!$str) {
  236. $str = "0.0";
  237. }
  238. return $str;
  239. }
  240. public function __toString(): string
  241. {
  242. return $this->format();
  243. }
  244. /**
  245. * Validates whether the argument is a valid complex number, converting scalar or array values if possible
  246. *
  247. * @param mixed $complex The value to validate
  248. * @return Complex
  249. * @throws Exception If the argument isn't a Complex number or cannot be converted to one
  250. */
  251. public static function validateComplexArgument($complex): Complex
  252. {
  253. if (is_scalar($complex) || is_array($complex)) {
  254. $complex = new Complex($complex);
  255. } elseif (!is_object($complex) || !($complex instanceof Complex)) {
  256. throw new Exception('Value is not a valid complex number');
  257. }
  258. return $complex;
  259. }
  260. /**
  261. * Returns the reverse of this complex number
  262. *
  263. * @return Complex
  264. */
  265. public function reverse(): Complex
  266. {
  267. return new Complex(
  268. $this->imaginaryPart,
  269. $this->realPart,
  270. ($this->realPart == 0.0) ? null : $this->suffix
  271. );
  272. }
  273. public function invertImaginary(): Complex
  274. {
  275. return new Complex(
  276. $this->realPart,
  277. $this->imaginaryPart * -1,
  278. ($this->imaginaryPart == 0.0) ? null : $this->suffix
  279. );
  280. }
  281. public function invertReal(): Complex
  282. {
  283. return new Complex(
  284. $this->realPart * -1,
  285. $this->imaginaryPart,
  286. ($this->imaginaryPart == 0.0) ? null : $this->suffix
  287. );
  288. }
  289. protected static $functions = [
  290. 'abs',
  291. 'acos',
  292. 'acosh',
  293. 'acot',
  294. 'acoth',
  295. 'acsc',
  296. 'acsch',
  297. 'argument',
  298. 'asec',
  299. 'asech',
  300. 'asin',
  301. 'asinh',
  302. 'atan',
  303. 'atanh',
  304. 'conjugate',
  305. 'cos',
  306. 'cosh',
  307. 'cot',
  308. 'coth',
  309. 'csc',
  310. 'csch',
  311. 'exp',
  312. 'inverse',
  313. 'ln',
  314. 'log2',
  315. 'log10',
  316. 'negative',
  317. 'pow',
  318. 'rho',
  319. 'sec',
  320. 'sech',
  321. 'sin',
  322. 'sinh',
  323. 'sqrt',
  324. 'tan',
  325. 'tanh',
  326. 'theta',
  327. ];
  328. protected static $operations = [
  329. 'add',
  330. 'subtract',
  331. 'multiply',
  332. 'divideby',
  333. 'divideinto',
  334. ];
  335. /**
  336. * Returns the result of the function call or operation
  337. *
  338. * @return Complex|float
  339. * @throws Exception|\InvalidArgumentException
  340. */
  341. public function __call($functionName, $arguments)
  342. {
  343. $functionName = strtolower(str_replace('_', '', $functionName));
  344. // Test for function calls
  345. if (in_array($functionName, self::$functions, true)) {
  346. return Functions::$functionName($this, ...$arguments);
  347. }
  348. // Test for operation calls
  349. if (in_array($functionName, self::$operations, true)) {
  350. return Operations::$functionName($this, ...$arguments);
  351. }
  352. throw new Exception('Complex Function or Operation does not exist');
  353. }
  354. }