Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 400 and 402] [Versions 400 and 403]

   1  <?php
   2  
   3  namespace Complex;
   4  
   5  use InvalidArgumentException;
   6  
   7  class Functions
   8  {
   9      /**
  10       * Returns the absolute value (modulus) of a complex number.
  11       * Also known as the rho of the complex number, i.e. the distance/radius
  12       *   from the centrepoint to the representation of the number in polar coordinates.
  13       *
  14       * This function is a synonym for rho()
  15       *
  16       * @param     Complex|mixed    $complex    Complex number or a numeric value.
  17       * @return    float            The absolute (or rho) value of the complex argument.
  18       * @throws    Exception        If argument isn't a valid real or complex number.
  19       *
  20       * @see    rho
  21       *
  22       */
  23      public static function abs($complex): float
  24      {
  25          return self::rho($complex);
  26      }
  27  
  28      /**
  29       * Returns the inverse cosine of a complex number.
  30       *
  31       * @param     Complex|mixed    $complex    Complex number or a numeric value.
  32       * @return    Complex          The inverse cosine of the complex argument.
  33       * @throws    Exception        If argument isn't a valid real or complex number.
  34       */
  35      public static function acos($complex): Complex
  36      {
  37          $complex = Complex::validateComplexArgument($complex);
  38  
  39          $square = clone $complex;
  40          $square = Operations::multiply($square, $complex);
  41          $invsqrt = new Complex(1.0);
  42          $invsqrt = Operations::subtract($invsqrt, $square);
  43          $invsqrt = self::sqrt($invsqrt);
  44          $adjust = new Complex(
  45              $complex->getReal() - $invsqrt->getImaginary(),
  46              $complex->getImaginary() + $invsqrt->getReal()
  47          );
  48          $log = self::ln($adjust);
  49  
  50          return new Complex(
  51              $log->getImaginary(),
  52              -1 * $log->getReal()
  53          );
  54      }
  55  
  56      /**
  57       * Returns the inverse hyperbolic cosine of a complex number.
  58       *
  59       * @param     Complex|mixed    $complex    Complex number or a numeric value.
  60       * @return    Complex          The inverse hyperbolic cosine of the complex argument.
  61       * @throws    Exception        If argument isn't a valid real or complex number.
  62       */
  63      public static function acosh($complex): Complex
  64      {
  65          $complex = Complex::validateComplexArgument($complex);
  66  
  67          if ($complex->isReal() && ($complex->getReal() > 1)) {
  68              return new Complex(\acosh($complex->getReal()));
  69          }
  70  
  71          $acosh = self::acos($complex)
  72              ->reverse();
  73          if ($acosh->getReal() < 0.0) {
  74              $acosh = $acosh->invertReal();
  75          }
  76  
  77          return $acosh;
  78      }
  79  
  80      /**
  81       * Returns the inverse cotangent of a complex number.
  82       *
  83       * @param     Complex|mixed    $complex    Complex number or a numeric value.
  84       * @return    Complex          The inverse cotangent of the complex argument.
  85       * @throws    Exception        If argument isn't a valid real or complex number.
  86       * @throws    \InvalidArgumentException    If function would result in a division by zero
  87       */
  88      public static function acot($complex): Complex
  89      {
  90          $complex = Complex::validateComplexArgument($complex);
  91  
  92          return self::atan(self::inverse($complex));
  93      }
  94  
  95      /**
  96       * Returns the inverse hyperbolic cotangent of a complex number.
  97       *
  98       * @param     Complex|mixed    $complex    Complex number or a numeric value.
  99       * @return    Complex          The inverse hyperbolic cotangent of the complex argument.
 100       * @throws    Exception        If argument isn't a valid real or complex number.
 101       * @throws    \InvalidArgumentException    If function would result in a division by zero
 102       */
 103      public static function acoth($complex): Complex
 104      {
 105          $complex = Complex::validateComplexArgument($complex);
 106  
 107          return self::atanh(self::inverse($complex));
 108      }
 109  
 110      /**
 111       * Returns the inverse cosecant of a complex number.
 112       *
 113       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 114       * @return    Complex          The inverse cosecant of the complex argument.
 115       * @throws    Exception        If argument isn't a valid real or complex number.
 116       * @throws    \InvalidArgumentException    If function would result in a division by zero
 117       */
 118      public static function acsc($complex): Complex
 119      {
 120          $complex = Complex::validateComplexArgument($complex);
 121  
 122          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
 123              return new Complex(INF);
 124          }
 125  
 126          return self::asin(self::inverse($complex));
 127      }
 128  
 129      /**
 130       * Returns the inverse hyperbolic cosecant of a complex number.
 131       *
 132       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 133       * @return    Complex          The inverse hyperbolic cosecant of the complex argument.
 134       * @throws    Exception        If argument isn't a valid real or complex number.
 135       * @throws    \InvalidArgumentException    If function would result in a division by zero
 136       */
 137      public static function acsch($complex): Complex
 138      {
 139          $complex = Complex::validateComplexArgument($complex);
 140  
 141          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
 142              return new Complex(INF);
 143          }
 144  
 145          return self::asinh(self::inverse($complex));
 146      }
 147  
 148      /**
 149       * Returns the argument of a complex number.
 150       * Also known as the theta of the complex number, i.e. the angle in radians
 151       *   from the real axis to the representation of the number in polar coordinates.
 152       *
 153       * This function is a synonym for theta()
 154       *
 155       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 156       * @return    float            The argument (or theta) value of the complex argument.
 157       * @throws    Exception        If argument isn't a valid real or complex number.
 158       *
 159       * @see    theta
 160       */
 161      public static function argument($complex): float
 162      {
 163          return self::theta($complex);
 164      }
 165  
 166      /**
 167       * Returns the inverse secant of a complex number.
 168       *
 169       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 170       * @return    Complex          The inverse secant of the complex argument.
 171       * @throws    Exception        If argument isn't a valid real or complex number.
 172       * @throws    \InvalidArgumentException    If function would result in a division by zero
 173       */
 174      public static function asec($complex): Complex
 175      {
 176          $complex = Complex::validateComplexArgument($complex);
 177  
 178          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
 179              return new Complex(INF);
 180          }
 181  
 182          return self::acos(self::inverse($complex));
 183      }
 184  
 185      /**
 186       * Returns the inverse hyperbolic secant of a complex number.
 187       *
 188       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 189       * @return    Complex          The inverse hyperbolic secant of the complex argument.
 190       * @throws    Exception        If argument isn't a valid real or complex number.
 191       * @throws    \InvalidArgumentException    If function would result in a division by zero
 192       */
 193      public static function asech($complex): Complex
 194      {
 195          $complex = Complex::validateComplexArgument($complex);
 196  
 197          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
 198              return new Complex(INF);
 199          }
 200  
 201          return self::acosh(self::inverse($complex));
 202      }
 203  
 204      /**
 205       * Returns the inverse sine of a complex number.
 206       *
 207       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 208       * @return    Complex          The inverse sine of the complex argument.
 209       * @throws    Exception        If argument isn't a valid real or complex number.
 210       */
 211      public static function asin($complex): Complex
 212      {
 213          $complex = Complex::validateComplexArgument($complex);
 214  
 215          $square = Operations::multiply($complex, $complex);
 216          $invsqrt = new Complex(1.0);
 217          $invsqrt = Operations::subtract($invsqrt, $square);
 218          $invsqrt = self::sqrt($invsqrt);
 219          $adjust = new Complex(
 220              $invsqrt->getReal() - $complex->getImaginary(),
 221              $invsqrt->getImaginary() + $complex->getReal()
 222          );
 223          $log = self::ln($adjust);
 224  
 225          return new Complex(
 226              $log->getImaginary(),
 227              -1 * $log->getReal()
 228          );
 229      }
 230  
 231      /**
 232       * Returns the inverse hyperbolic sine of a complex number.
 233       *
 234       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 235       * @return    Complex          The inverse hyperbolic sine of the complex argument.
 236       * @throws    Exception        If argument isn't a valid real or complex number.
 237       */
 238      public static function asinh($complex): Complex
 239      {
 240          $complex = Complex::validateComplexArgument($complex);
 241  
 242          if ($complex->isReal() && ($complex->getReal() > 1)) {
 243              return new Complex(\asinh($complex->getReal()));
 244          }
 245  
 246          $asinh = clone $complex;
 247          $asinh = $asinh->reverse()
 248              ->invertReal();
 249          $asinh = self::asin($asinh);
 250  
 251          return $asinh->reverse()
 252              ->invertImaginary();
 253      }
 254  
 255      /**
 256       * Returns the inverse tangent of a complex number.
 257       *
 258       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 259       * @return    Complex          The inverse tangent of the complex argument.
 260       * @throws    Exception        If argument isn't a valid real or complex number.
 261       * @throws    \InvalidArgumentException    If function would result in a division by zero
 262       */
 263      public static function atan($complex): Complex
 264      {
 265          $complex = Complex::validateComplexArgument($complex);
 266  
 267          if ($complex->isReal()) {
 268              return new Complex(\atan($complex->getReal()));
 269          }
 270  
 271          $t1Value = new Complex(-1 * $complex->getImaginary(), $complex->getReal());
 272          $uValue = new Complex(1, 0);
 273  
 274          $d1Value = clone $uValue;
 275          $d1Value = Operations::subtract($d1Value, $t1Value);
 276          $d2Value = Operations::add($t1Value, $uValue);
 277          $uResult = $d1Value->divideBy($d2Value);
 278          $uResult = self::ln($uResult);
 279  
 280          return new Complex(
 281              (($uResult->getImaginary() == M_PI) ? -M_PI : $uResult->getImaginary()) * -0.5,
 282              $uResult->getReal() * 0.5,
 283              $complex->getSuffix()
 284          );
 285      }
 286  
 287      /**
 288       * Returns the inverse hyperbolic tangent of a complex number.
 289       *
 290       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 291       * @return    Complex          The inverse hyperbolic tangent of the complex argument.
 292       * @throws    Exception        If argument isn't a valid real or complex number.
 293       */
 294      public static function atanh($complex): Complex
 295      {
 296          $complex = Complex::validateComplexArgument($complex);
 297  
 298          if ($complex->isReal()) {
 299              $real = $complex->getReal();
 300              if ($real >= -1.0 && $real <= 1.0) {
 301                  return new Complex(\atanh($real));
 302              } else {
 303                  return new Complex(\atanh(1 / $real), (($real < 0.0) ? M_PI_2 : -1 * M_PI_2));
 304              }
 305          }
 306  
 307          $iComplex = clone $complex;
 308          $iComplex = $iComplex->invertImaginary()
 309              ->reverse();
 310          return self::atan($iComplex)
 311              ->invertReal()
 312              ->reverse();
 313      }
 314  
 315      /**
 316       * Returns the complex conjugate of a complex number
 317       *
 318       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 319       * @return    Complex          The conjugate of the complex argument.
 320       * @throws    Exception        If argument isn't a valid real or complex number.
 321       */
 322      public static function conjugate($complex): Complex
 323      {
 324          $complex = Complex::validateComplexArgument($complex);
 325  
 326          return new Complex(
 327              $complex->getReal(),
 328              -1 * $complex->getImaginary(),
 329              $complex->getSuffix()
 330          );
 331      }
 332  
 333      /**
 334       * Returns the cosine of a complex number.
 335       *
 336       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 337       * @return    Complex          The cosine of the complex argument.
 338       * @throws    Exception        If argument isn't a valid real or complex number.
 339       */
 340      public static function cos($complex): Complex
 341      {
 342          $complex = Complex::validateComplexArgument($complex);
 343  
 344          if ($complex->isReal()) {
 345              return new Complex(\cos($complex->getReal()));
 346          }
 347  
 348          return self::conjugate(
 349              new Complex(
 350                  \cos($complex->getReal()) * \cosh($complex->getImaginary()),
 351                  \sin($complex->getReal()) * \sinh($complex->getImaginary()),
 352                  $complex->getSuffix()
 353              )
 354          );
 355      }
 356  
 357      /**
 358       * Returns the hyperbolic cosine of a complex number.
 359       *
 360       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 361       * @return    Complex          The hyperbolic cosine of the complex argument.
 362       * @throws    Exception        If argument isn't a valid real or complex number.
 363       */
 364      public static function cosh($complex): Complex
 365      {
 366          $complex = Complex::validateComplexArgument($complex);
 367  
 368          if ($complex->isReal()) {
 369              return new Complex(\cosh($complex->getReal()));
 370          }
 371  
 372          return new Complex(
 373              \cosh($complex->getReal()) * \cos($complex->getImaginary()),
 374              \sinh($complex->getReal()) * \sin($complex->getImaginary()),
 375              $complex->getSuffix()
 376          );
 377      }
 378  
 379      /**
 380       * Returns the cotangent of a complex number.
 381       *
 382       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 383       * @return    Complex          The cotangent of the complex argument.
 384       * @throws    Exception        If argument isn't a valid real or complex number.
 385       * @throws    \InvalidArgumentException    If function would result in a division by zero
 386       */
 387      public static function cot($complex): Complex
 388      {
 389          $complex = Complex::validateComplexArgument($complex);
 390  
 391          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
 392              return new Complex(INF);
 393          }
 394  
 395          return self::inverse(self::tan($complex));
 396      }
 397  
 398      /**
 399       * Returns the hyperbolic cotangent of a complex number.
 400       *
 401       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 402       * @return    Complex          The hyperbolic cotangent of the complex argument.
 403       * @throws    Exception        If argument isn't a valid real or complex number.
 404       * @throws    \InvalidArgumentException    If function would result in a division by zero
 405       */
 406      public static function coth($complex): Complex
 407      {
 408          $complex = Complex::validateComplexArgument($complex);
 409  
 410          return self::inverse(self::tanh($complex));
 411      }
 412  
 413      /**
 414       * Returns the cosecant of a complex number.
 415       *
 416       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 417       * @return    Complex          The cosecant of the complex argument.
 418       * @throws    Exception        If argument isn't a valid real or complex number.
 419       * @throws    \InvalidArgumentException    If function would result in a division by zero
 420       */
 421      public static function csc($complex): Complex
 422      {
 423          $complex = Complex::validateComplexArgument($complex);
 424  
 425          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
 426              return new Complex(INF);
 427          }
 428  
 429          return self::inverse(self::sin($complex));
 430      }
 431  
 432      /**
 433       * Returns the hyperbolic cosecant of a complex number.
 434       *
 435       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 436       * @return    Complex          The hyperbolic cosecant of the complex argument.
 437       * @throws    Exception        If argument isn't a valid real or complex number.
 438       * @throws    \InvalidArgumentException    If function would result in a division by zero
 439       */
 440      public static function csch($complex): Complex
 441      {
 442          $complex = Complex::validateComplexArgument($complex);
 443  
 444          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
 445              return new Complex(INF);
 446          }
 447  
 448          return self::inverse(self::sinh($complex));
 449      }
 450  
 451      /**
 452       * Returns the exponential of a complex number.
 453       *
 454       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 455       * @return    Complex          The exponential of the complex argument.
 456       * @throws    Exception        If argument isn't a valid real or complex number.
 457       */
 458      public static function exp($complex): Complex
 459      {
 460          $complex = Complex::validateComplexArgument($complex);
 461  
 462          if (($complex->getReal() == 0.0) && (\abs($complex->getImaginary()) == M_PI)) {
 463              return new Complex(-1.0, 0.0);
 464          }
 465  
 466          $rho = \exp($complex->getReal());
 467  
 468          return new Complex(
 469              $rho * \cos($complex->getImaginary()),
 470              $rho * \sin($complex->getImaginary()),
 471              $complex->getSuffix()
 472          );
 473      }
 474  
 475      /**
 476       * Returns the inverse of a complex number.
 477       *
 478       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 479       * @return    Complex          The inverse of the complex argument.
 480       * @throws    Exception        If argument isn't a valid real or complex number.
 481       * @throws    InvalidArgumentException    If function would result in a division by zero
 482       */
 483      public static function inverse($complex): Complex
 484      {
 485          $complex = clone Complex::validateComplexArgument($complex);
 486  
 487          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
 488              throw new InvalidArgumentException('Division by zero');
 489          }
 490  
 491          return $complex->divideInto(1.0);
 492      }
 493  
 494      /**
 495       * Returns the natural logarithm of a complex number.
 496       *
 497       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 498       * @return    Complex          The natural logarithm of the complex argument.
 499       * @throws    Exception        If argument isn't a valid real or complex number.
 500       * @throws    InvalidArgumentException  If the real and the imaginary parts are both zero
 501       */
 502      public static function ln($complex): Complex
 503      {
 504          $complex = Complex::validateComplexArgument($complex);
 505  
 506          if (($complex->getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
 507              throw new InvalidArgumentException();
 508          }
 509  
 510          return new Complex(
 511              \log(self::rho($complex)),
 512              self::theta($complex),
 513              $complex->getSuffix()
 514          );
 515      }
 516  
 517      /**
 518       * Returns the base-2 logarithm of a complex number.
 519       *
 520       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 521       * @return    Complex          The base-2 logarithm of the complex argument.
 522       * @throws    Exception        If argument isn't a valid real or complex number.
 523       * @throws    InvalidArgumentException  If the real and the imaginary parts are both zero
 524       */
 525      public static function log2($complex): Complex
 526      {
 527          $complex = Complex::validateComplexArgument($complex);
 528  
 529          if (($complex->getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
 530              throw new InvalidArgumentException();
 531          } elseif (($complex->getReal() > 0.0) && ($complex->getImaginary() == 0.0)) {
 532              return new Complex(\log($complex->getReal(), 2), 0.0, $complex->getSuffix());
 533          }
 534  
 535          return self::ln($complex)
 536              ->multiply(\log(Complex::EULER, 2));
 537      }
 538  
 539      /**
 540       * Returns the common logarithm (base 10) of a complex number.
 541       *
 542       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 543       * @return    Complex          The common logarithm (base 10) of the complex argument.
 544       * @throws    Exception        If argument isn't a valid real or complex number.
 545       * @throws    InvalidArgumentException  If the real and the imaginary parts are both zero
 546       */
 547      public static function log10($complex): Complex
 548      {
 549          $complex = Complex::validateComplexArgument($complex);
 550  
 551          if (($complex->getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
 552              throw new InvalidArgumentException();
 553          } elseif (($complex->getReal() > 0.0) && ($complex->getImaginary() == 0.0)) {
 554              return new Complex(\log10($complex->getReal()), 0.0, $complex->getSuffix());
 555          }
 556  
 557          return self::ln($complex)
 558              ->multiply(\log10(Complex::EULER));
 559      }
 560  
 561      /**
 562       * Returns the negative of a complex number.
 563       *
 564       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 565       * @return    Complex          The negative value of the complex argument.
 566       * @throws    Exception        If argument isn't a valid real or complex number.
 567       *
 568       * @see    rho
 569       *
 570       */
 571      public static function negative($complex): Complex
 572      {
 573          $complex = Complex::validateComplexArgument($complex);
 574  
 575          return new Complex(
 576              -1 * $complex->getReal(),
 577              -1 * $complex->getImaginary(),
 578              $complex->getSuffix()
 579          );
 580      }
 581  
 582      /**
 583       * Returns a complex number raised to a power.
 584       *
 585       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 586       * @param     float|integer    $power      The power to raise this value to
 587       * @return    Complex          The complex argument raised to the real power.
 588       * @throws    Exception        If the power argument isn't a valid real
 589       */
 590      public static function pow($complex, $power): Complex
 591      {
 592          $complex = Complex::validateComplexArgument($complex);
 593  
 594          if (!is_numeric($power)) {
 595              throw new Exception('Power argument must be a real number');
 596          }
 597  
 598          if ($complex->getImaginary() == 0.0 && $complex->getReal() >= 0.0) {
 599              return new Complex(\pow($complex->getReal(), $power));
 600          }
 601  
 602          $rValue = \sqrt(($complex->getReal() * $complex->getReal()) + ($complex->getImaginary() * $complex->getImaginary()));
 603          $rPower = \pow($rValue, $power);
 604          $theta = $complex->argument() * $power;
 605          if ($theta == 0) {
 606              return new Complex(1);
 607          }
 608  
 609          return new Complex($rPower * \cos($theta), $rPower * \sin($theta), $complex->getSuffix());
 610      }
 611  
 612      /**
 613       * Returns the rho of a complex number.
 614       * This is the distance/radius from the centrepoint to the representation of the number in polar coordinates.
 615       *
 616       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 617       * @return    float            The rho value of the complex argument.
 618       * @throws    Exception        If argument isn't a valid real or complex number.
 619       */
 620      public static function rho($complex): float
 621      {
 622          $complex = Complex::validateComplexArgument($complex);
 623  
 624          return \sqrt(
 625              ($complex->getReal() * $complex->getReal()) +
 626              ($complex->getImaginary() * $complex->getImaginary())
 627          );
 628      }
 629  
 630      /**
 631       * Returns the secant of a complex number.
 632       *
 633       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 634       * @return    Complex          The secant of the complex argument.
 635       * @throws    Exception        If argument isn't a valid real or complex number.
 636       * @throws    \InvalidArgumentException    If function would result in a division by zero
 637       */
 638      public static function sec($complex): Complex
 639      {
 640          $complex = Complex::validateComplexArgument($complex);
 641  
 642          return self::inverse(self::cos($complex));
 643      }
 644  
 645      /**
 646       * Returns the hyperbolic secant of a complex number.
 647       *
 648       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 649       * @return    Complex          The hyperbolic secant of the complex argument.
 650       * @throws    Exception        If argument isn't a valid real or complex number.
 651       * @throws    \InvalidArgumentException    If function would result in a division by zero
 652       */
 653      public static function sech($complex): Complex
 654      {
 655          $complex = Complex::validateComplexArgument($complex);
 656  
 657          return self::inverse(self::cosh($complex));
 658      }
 659  
 660      /**
 661       * Returns the sine of a complex number.
 662       *
 663       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 664       * @return    Complex          The sine of the complex argument.
 665       * @throws    Exception        If argument isn't a valid real or complex number.
 666       */
 667      public static function sin($complex): Complex
 668      {
 669          $complex = Complex::validateComplexArgument($complex);
 670  
 671          if ($complex->isReal()) {
 672              return new Complex(\sin($complex->getReal()));
 673          }
 674  
 675          return new Complex(
 676              \sin($complex->getReal()) * \cosh($complex->getImaginary()),
 677              \cos($complex->getReal()) * \sinh($complex->getImaginary()),
 678              $complex->getSuffix()
 679          );
 680      }
 681  
 682      /**
 683       * Returns the hyperbolic sine of a complex number.
 684       *
 685       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 686       * @return    Complex          The hyperbolic sine of the complex argument.
 687       * @throws    Exception        If argument isn't a valid real or complex number.
 688       */
 689      public static function sinh($complex): Complex
 690      {
 691          $complex = Complex::validateComplexArgument($complex);
 692  
 693          if ($complex->isReal()) {
 694              return new Complex(\sinh($complex->getReal()));
 695          }
 696  
 697          return new Complex(
 698              \sinh($complex->getReal()) * \cos($complex->getImaginary()),
 699              \cosh($complex->getReal()) * \sin($complex->getImaginary()),
 700              $complex->getSuffix()
 701          );
 702      }
 703  
 704      /**
 705       * Returns the square root of a complex number.
 706       *
 707       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 708       * @return    Complex          The Square root of the complex argument.
 709       * @throws    Exception        If argument isn't a valid real or complex number.
 710       */
 711      public static function sqrt($complex): Complex
 712      {
 713          $complex = Complex::validateComplexArgument($complex);
 714  
 715          $theta = self::theta($complex);
 716          $delta1 = \cos($theta / 2);
 717          $delta2 = \sin($theta / 2);
 718          $rho = \sqrt(self::rho($complex));
 719  
 720          return new Complex($delta1 * $rho, $delta2 * $rho, $complex->getSuffix());
 721      }
 722  
 723      /**
 724       * Returns the tangent of a complex number.
 725       *
 726       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 727       * @return    Complex          The tangent of the complex argument.
 728       * @throws    Exception        If argument isn't a valid real or complex number.
 729       * @throws    InvalidArgumentException    If function would result in a division by zero
 730       */
 731      public static function tan($complex): Complex
 732      {
 733          $complex = Complex::validateComplexArgument($complex);
 734  
 735          if ($complex->isReal()) {
 736              return new Complex(\tan($complex->getReal()));
 737          }
 738  
 739          $real = $complex->getReal();
 740          $imaginary = $complex->getImaginary();
 741          $divisor = 1 + \pow(\tan($real), 2) * \pow(\tanh($imaginary), 2);
 742          if ($divisor == 0.0) {
 743              throw new InvalidArgumentException('Division by zero');
 744          }
 745  
 746          return new Complex(
 747              \pow(self::sech($imaginary)->getReal(), 2) * \tan($real) / $divisor,
 748              \pow(self::sec($real)->getReal(), 2) * \tanh($imaginary) / $divisor,
 749              $complex->getSuffix()
 750          );
 751      }
 752  
 753      /**
 754       * Returns the hyperbolic tangent of a complex number.
 755       *
 756       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 757       * @return    Complex          The hyperbolic tangent of the complex argument.
 758       * @throws    Exception        If argument isn't a valid real or complex number.
 759       * @throws    \InvalidArgumentException    If function would result in a division by zero
 760       */
 761      public static function tanh($complex): Complex
 762      {
 763          $complex = Complex::validateComplexArgument($complex);
 764          $real = $complex->getReal();
 765          $imaginary = $complex->getImaginary();
 766          $divisor = \cos($imaginary) * \cos($imaginary) + \sinh($real) * \sinh($real);
 767          if ($divisor == 0.0) {
 768              throw new InvalidArgumentException('Division by zero');
 769          }
 770  
 771          return new Complex(
 772              \sinh($real) * \cosh($real) / $divisor,
 773              0.5 * \sin(2 * $imaginary) / $divisor,
 774              $complex->getSuffix()
 775          );
 776      }
 777  
 778      /**
 779       * Returns the theta of a complex number.
 780       *   This is the angle in radians from the real axis to the representation of the number in polar coordinates.
 781       *
 782       * @param     Complex|mixed    $complex    Complex number or a numeric value.
 783       * @return    float            The theta value of the complex argument.
 784       * @throws    Exception        If argument isn't a valid real or complex number.
 785       */
 786      public static function theta($complex): float
 787      {
 788          $complex = Complex::validateComplexArgument($complex);
 789  
 790          if ($complex->getReal() == 0.0) {
 791              if ($complex->isReal()) {
 792                  return 0.0;
 793              } elseif ($complex->getImaginary() < 0.0) {
 794                  return M_PI / -2;
 795              }
 796              return M_PI / 2;
 797          } elseif ($complex->getReal() > 0.0) {
 798              return \atan($complex->getImaginary() / $complex->getReal());
 799          } elseif ($complex->getImaginary() < 0.0) {
 800              return -(M_PI - \atan(\abs($complex->getImaginary()) / \abs($complex->getReal())));
 801          }
 802  
 803          return M_PI - \atan($complex->getImaginary() / \abs($complex->getReal()));
 804      }
 805  }