Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

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

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