simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

complex.js (30643B)


      1 /**
      2  * @license Complex.js v2.1.1 12/05/2020
      3  *
      4  * Copyright (c) 2020, Robert Eisele (robert@xarg.org)
      5  * Dual licensed under the MIT or GPL Version 2 licenses.
      6  **/
      7 
      8 /**
      9  *
     10  * This class allows the manipulation of complex numbers.
     11  * You can pass a complex number in different formats. Either as object, double, string or two integer parameters.
     12  *
     13  * Object form
     14  * { re: <real>, im: <imaginary> }
     15  * { arg: <angle>, abs: <radius> }
     16  * { phi: <angle>, r: <radius> }
     17  *
     18  * Array / Vector form
     19  * [ real, imaginary ]
     20  *
     21  * Double form
     22  * 99.3 - Single double value
     23  *
     24  * String form
     25  * '23.1337' - Simple real number
     26  * '15+3i' - a simple complex number
     27  * '3-i' - a simple complex number
     28  *
     29  * Example:
     30  *
     31  * var c = new Complex('99.3+8i');
     32  * c.mul({r: 3, i: 9}).div(4.9).sub(3, 2);
     33  *
     34  */
     35 
     36 (function(root) {
     37 
     38   'use strict';
     39 
     40   var cosh = Math.cosh || function(x) {
     41     return Math.abs(x) < 1e-9 ? 1 - x : (Math.exp(x) + Math.exp(-x)) * 0.5;
     42   };
     43 
     44   var sinh = Math.sinh || function(x) {
     45     return Math.abs(x) < 1e-9 ? x : (Math.exp(x) - Math.exp(-x)) * 0.5;
     46   };
     47 
     48   /**
     49    * Calculates cos(x) - 1 using Taylor series if x is small (-¼π ≤ x ≤ ¼π).
     50    *
     51    * @param {number} x
     52    * @returns {number} cos(x) - 1
     53    */
     54   var cosm1 = function(x) {
     55 
     56     var b = Math.PI / 4;
     57     if (-b > x || x > b) {
     58       return Math.cos(x) - 1.0;
     59     }
     60 
     61     /* Calculate horner form of polynomial of taylor series in Q
     62     var fac = 1, alt = 1, pol = {};
     63     for (var i = 0; i <= 16; i++) {
     64       fac*= i || 1;
     65       if (i % 2 == 0) {
     66         pol[i] = new Fraction(1, alt * fac);
     67         alt = -alt;
     68       }
     69     }
     70     console.log(new Polynomial(pol).toHorner()); // (((((((1/20922789888000x^2-1/87178291200)x^2+1/479001600)x^2-1/3628800)x^2+1/40320)x^2-1/720)x^2+1/24)x^2-1/2)x^2+1
     71     */
     72 
     73     var xx = x * x;
     74     return xx * (
     75       xx * (
     76         xx * (
     77           xx * (
     78             xx * (
     79               xx * (
     80                 xx * (
     81                   xx / 20922789888000
     82                   - 1 / 87178291200)
     83                 + 1 / 479001600)
     84               - 1 / 3628800)
     85             + 1 / 40320)
     86           - 1 / 720)
     87         + 1 / 24)
     88       - 1 / 2);
     89   };
     90 
     91   var hypot = function(x, y) {
     92 
     93     var a = Math.abs(x);
     94     var b = Math.abs(y);
     95 
     96     if (a < 3000 && b < 3000) {
     97       return Math.sqrt(a * a + b * b);
     98     }
     99 
    100     if (a < b) {
    101       a = b;
    102       b = x / y;
    103     } else {
    104       b = y / x;
    105     }
    106     return a * Math.sqrt(1 + b * b);
    107   };
    108 
    109   var parser_exit = function() {
    110     throw SyntaxError('Invalid Param');
    111   };
    112 
    113   /**
    114    * Calculates log(sqrt(a^2+b^2)) in a way to avoid overflows
    115    *
    116    * @param {number} a
    117    * @param {number} b
    118    * @returns {number}
    119    */
    120   function logHypot(a, b) {
    121 
    122     var _a = Math.abs(a);
    123     var _b = Math.abs(b);
    124 
    125     if (a === 0) {
    126       return Math.log(_b);
    127     }
    128 
    129     if (b === 0) {
    130       return Math.log(_a);
    131     }
    132 
    133     if (_a < 3000 && _b < 3000) {
    134       return Math.log(a * a + b * b) * 0.5;
    135     }
    136 
    137     /* I got 4 ideas to compute this property without overflow:
    138      *
    139      * Testing 1000000 times with random samples for a,b ∈ [1, 1000000000] against a big decimal library to get an error estimate
    140      *
    141      * 1. Only eliminate the square root: (OVERALL ERROR: 3.9122483030951116e-11)
    142 
    143      Math.log(a * a + b * b) / 2
    144 
    145      *
    146      *
    147      * 2. Try to use the non-overflowing pythagoras: (OVERALL ERROR: 8.889760039210159e-10)
    148 
    149      var fn = function(a, b) {
    150      a = Math.abs(a);
    151      b = Math.abs(b);
    152      var t = Math.min(a, b);
    153      a = Math.max(a, b);
    154      t = t / a;
    155 
    156      return Math.log(a) + Math.log(1 + t * t) / 2;
    157      };
    158 
    159      * 3. Abuse the identity cos(atan(y/x) = x / sqrt(x^2+y^2): (OVERALL ERROR: 3.4780178737037204e-10)
    160 
    161      Math.log(a / Math.cos(Math.atan2(b, a)))
    162 
    163      * 4. Use 3. and apply log rules: (OVERALL ERROR: 1.2014087502620896e-9)
    164 
    165      Math.log(a) - Math.log(Math.cos(Math.atan2(b, a)))
    166 
    167      */
    168 
    169      a = a / 2;
    170      b = b / 2;
    171 
    172     return 0.5 * Math.log(a * a + b * b) + Math.LN2;
    173   }
    174 
    175   var parse = function(a, b) {
    176 
    177     var z = { 're': 0, 'im': 0 };
    178 
    179     if (a === undefined || a === null) {
    180       z['re'] =
    181       z['im'] = 0;
    182     } else if (b !== undefined) {
    183       z['re'] = a;
    184       z['im'] = b;
    185     } else
    186       switch (typeof a) {
    187 
    188         case 'object':
    189 
    190           if ('im' in a && 're' in a) {
    191             z['re'] = a['re'];
    192             z['im'] = a['im'];
    193           } else if ('abs' in a && 'arg' in a) {
    194             if (!Number.isFinite(a['abs']) && Number.isFinite(a['arg'])) {
    195               return Complex['INFINITY'];
    196             }
    197             z['re'] = a['abs'] * Math.cos(a['arg']);
    198             z['im'] = a['abs'] * Math.sin(a['arg']);
    199           } else if ('r' in a && 'phi' in a) {
    200             if (!Number.isFinite(a['r']) && Number.isFinite(a['phi'])) {
    201               return Complex['INFINITY'];
    202             }
    203             z['re'] = a['r'] * Math.cos(a['phi']);
    204             z['im'] = a['r'] * Math.sin(a['phi']);
    205           } else if (a.length === 2) { // Quick array check
    206             z['re'] = a[0];
    207             z['im'] = a[1];
    208           } else {
    209             parser_exit();
    210           }
    211           break;
    212 
    213         case 'string':
    214 
    215           z['im'] = /* void */
    216           z['re'] = 0;
    217 
    218           var tokens = a.match(/\d+\.?\d*e[+-]?\d+|\d+\.?\d*|\.\d+|./g);
    219           var plus = 1;
    220           var minus = 0;
    221 
    222           if (tokens === null) {
    223             parser_exit();
    224           }
    225 
    226           for (var i = 0; i < tokens.length; i++) {
    227 
    228             var c = tokens[i];
    229 
    230             if (c === ' ' || c === '\t' || c === '\n') {
    231               /* void */
    232             } else if (c === '+') {
    233               plus++;
    234             } else if (c === '-') {
    235               minus++;
    236             } else if (c === 'i' || c === 'I') {
    237 
    238               if (plus + minus === 0) {
    239                 parser_exit();
    240               }
    241 
    242               if (tokens[i + 1] !== ' ' && !isNaN(tokens[i + 1])) {
    243                 z['im'] += parseFloat((minus % 2 ? '-' : '') + tokens[i + 1]);
    244                 i++;
    245               } else {
    246                 z['im'] += parseFloat((minus % 2 ? '-' : '') + '1');
    247               }
    248               plus = minus = 0;
    249 
    250             } else {
    251 
    252               if (plus + minus === 0 || isNaN(c)) {
    253                 parser_exit();
    254               }
    255 
    256               if (tokens[i + 1] === 'i' || tokens[i + 1] === 'I') {
    257                 z['im'] += parseFloat((minus % 2 ? '-' : '') + c);
    258                 i++;
    259               } else {
    260                 z['re'] += parseFloat((minus % 2 ? '-' : '') + c);
    261               }
    262               plus = minus = 0;
    263             }
    264           }
    265 
    266           // Still something on the stack
    267           if (plus + minus > 0) {
    268             parser_exit();
    269           }
    270           break;
    271 
    272         case 'number':
    273           z['im'] = 0;
    274           z['re'] = a;
    275           break;
    276 
    277         default:
    278           parser_exit();
    279       }
    280 
    281     if (isNaN(z['re']) || isNaN(z['im'])) {
    282       // If a calculation is NaN, we treat it as NaN and don't throw
    283       //parser_exit();
    284     }
    285 
    286     return z;
    287   };
    288 
    289   /**
    290    * @constructor
    291    * @returns {Complex}
    292    */
    293   function Complex(a, b) {
    294 
    295     if (!(this instanceof Complex)) {
    296       return new Complex(a, b);
    297     }
    298 
    299     var z = parse(a, b);
    300 
    301     this['re'] = z['re'];
    302     this['im'] = z['im'];
    303   }
    304 
    305   Complex.prototype = {
    306 
    307     're': 0,
    308     'im': 0,
    309 
    310     /**
    311      * Calculates the sign of a complex number, which is a normalized complex
    312      *
    313      * @returns {Complex}
    314      */
    315     'sign': function() {
    316 
    317       var abs = this['abs']();
    318 
    319       return new Complex(
    320         this['re'] / abs,
    321         this['im'] / abs);
    322     },
    323 
    324     /**
    325      * Adds two complex numbers
    326      *
    327      * @returns {Complex}
    328      */
    329     'add': function(a, b) {
    330 
    331       var z = new Complex(a, b);
    332 
    333       // Infinity + Infinity = NaN
    334       if (this['isInfinite']() && z['isInfinite']()) {
    335         return Complex['NAN'];
    336       }
    337 
    338       // Infinity + z = Infinity { where z != Infinity }
    339       if (this['isInfinite']() || z['isInfinite']()) {
    340         return Complex['INFINITY'];
    341       }
    342 
    343       return new Complex(
    344         this['re'] + z['re'],
    345         this['im'] + z['im']);
    346     },
    347 
    348     /**
    349      * Subtracts two complex numbers
    350      *
    351      * @returns {Complex}
    352      */
    353     'sub': function(a, b) {
    354 
    355       var z = new Complex(a, b);
    356 
    357       // Infinity - Infinity = NaN
    358       if (this['isInfinite']() && z['isInfinite']()) {
    359         return Complex['NAN'];
    360       }
    361 
    362       // Infinity - z = Infinity { where z != Infinity }
    363       if (this['isInfinite']() || z['isInfinite']()) {
    364         return Complex['INFINITY'];
    365       }
    366 
    367       return new Complex(
    368         this['re'] - z['re'],
    369         this['im'] - z['im']);
    370     },
    371 
    372     /**
    373      * Multiplies two complex numbers
    374      *
    375      * @returns {Complex}
    376      */
    377     'mul': function(a, b) {
    378 
    379       var z = new Complex(a, b);
    380 
    381       // Infinity * 0 = NaN
    382       if ((this['isInfinite']() && z['isZero']()) || (this['isZero']() && z['isInfinite']())) {
    383         return Complex['NAN'];
    384       }
    385 
    386       // Infinity * z = Infinity { where z != 0 }
    387       if (this['isInfinite']() || z['isInfinite']()) {
    388         return Complex['INFINITY'];
    389       }
    390 
    391       // Short circuit for real values
    392       if (z['im'] === 0 && this['im'] === 0) {
    393         return new Complex(this['re'] * z['re'], 0);
    394       }
    395 
    396       return new Complex(
    397         this['re'] * z['re'] - this['im'] * z['im'],
    398         this['re'] * z['im'] + this['im'] * z['re']);
    399     },
    400 
    401     /**
    402      * Divides two complex numbers
    403      *
    404      * @returns {Complex}
    405      */
    406     'div': function(a, b) {
    407 
    408       var z = new Complex(a, b);
    409 
    410       // 0 / 0 = NaN and Infinity / Infinity = NaN
    411       if ((this['isZero']() && z['isZero']()) || (this['isInfinite']() && z['isInfinite']())) {
    412         return Complex['NAN'];
    413       }
    414 
    415       // Infinity / 0 = Infinity
    416       if (this['isInfinite']() || z['isZero']()) {
    417         return Complex['INFINITY'];
    418       }
    419 
    420       // 0 / Infinity = 0
    421       if (this['isZero']() || z['isInfinite']()) {
    422         return Complex['ZERO'];
    423       }
    424 
    425       a = this['re'];
    426       b = this['im'];
    427 
    428       var c = z['re'];
    429       var d = z['im'];
    430       var t, x;
    431 
    432       if (0 === d) {
    433         // Divisor is real
    434         return new Complex(a / c, b / c);
    435       }
    436 
    437       if (Math.abs(c) < Math.abs(d)) {
    438 
    439         x = c / d;
    440         t = c * x + d;
    441 
    442         return new Complex(
    443           (a * x + b) / t,
    444           (b * x - a) / t);
    445 
    446       } else {
    447 
    448         x = d / c;
    449         t = d * x + c;
    450 
    451         return new Complex(
    452           (a + b * x) / t,
    453           (b - a * x) / t);
    454       }
    455     },
    456 
    457     /**
    458      * Calculate the power of two complex numbers
    459      *
    460      * @returns {Complex}
    461      */
    462     'pow': function(a, b) {
    463 
    464       var z = new Complex(a, b);
    465 
    466       a = this['re'];
    467       b = this['im'];
    468 
    469       if (z['isZero']()) {
    470         return Complex['ONE'];
    471       }
    472 
    473       // If the exponent is real
    474       if (z['im'] === 0) {
    475 
    476         if (b === 0 && a > 0) {
    477 
    478           return new Complex(Math.pow(a, z['re']), 0);
    479 
    480         } else if (a === 0) { // If base is fully imaginary
    481 
    482           switch ((z['re'] % 4 + 4) % 4) {
    483             case 0:
    484               return new Complex(Math.pow(b, z['re']), 0);
    485             case 1:
    486               return new Complex(0, Math.pow(b, z['re']));
    487             case 2:
    488               return new Complex(-Math.pow(b, z['re']), 0);
    489             case 3:
    490               return new Complex(0, -Math.pow(b, z['re']));
    491           }
    492         }
    493       }
    494 
    495       /* I couldn't find a good formula, so here is a derivation and optimization
    496        *
    497        * z_1^z_2 = (a + bi)^(c + di)
    498        *         = exp((c + di) * log(a + bi)
    499        *         = pow(a^2 + b^2, (c + di) / 2) * exp(i(c + di)atan2(b, a))
    500        * =>...
    501        * Re = (pow(a^2 + b^2, c / 2) * exp(-d * atan2(b, a))) * cos(d * log(a^2 + b^2) / 2 + c * atan2(b, a))
    502        * Im = (pow(a^2 + b^2, c / 2) * exp(-d * atan2(b, a))) * sin(d * log(a^2 + b^2) / 2 + c * atan2(b, a))
    503        *
    504        * =>...
    505        * Re = exp(c * log(sqrt(a^2 + b^2)) - d * atan2(b, a)) * cos(d * log(sqrt(a^2 + b^2)) + c * atan2(b, a))
    506        * Im = exp(c * log(sqrt(a^2 + b^2)) - d * atan2(b, a)) * sin(d * log(sqrt(a^2 + b^2)) + c * atan2(b, a))
    507        *
    508        * =>
    509        * Re = exp(c * logsq2 - d * arg(z_1)) * cos(d * logsq2 + c * arg(z_1))
    510        * Im = exp(c * logsq2 - d * arg(z_1)) * sin(d * logsq2 + c * arg(z_1))
    511        *
    512        */
    513 
    514       if (a === 0 && b === 0 && z['re'] > 0 && z['im'] >= 0) {
    515         return Complex['ZERO'];
    516       }
    517 
    518       var arg = Math.atan2(b, a);
    519       var loh = logHypot(a, b);
    520 
    521       a = Math.exp(z['re'] * loh - z['im'] * arg);
    522       b = z['im'] * loh + z['re'] * arg;
    523       return new Complex(
    524         a * Math.cos(b),
    525         a * Math.sin(b));
    526     },
    527 
    528     /**
    529      * Calculate the complex square root
    530      *
    531      * @returns {Complex}
    532      */
    533     'sqrt': function() {
    534 
    535       var a = this['re'];
    536       var b = this['im'];
    537       var r = this['abs']();
    538 
    539       var re, im;
    540 
    541       if (a >= 0) {
    542 
    543         if (b === 0) {
    544           return new Complex(Math.sqrt(a), 0);
    545         }
    546 
    547         re = 0.5 * Math.sqrt(2.0 * (r + a));
    548       } else {
    549         re = Math.abs(b) / Math.sqrt(2 * (r - a));
    550       }
    551 
    552       if (a <= 0) {
    553         im = 0.5 * Math.sqrt(2.0 * (r - a));
    554       } else {
    555         im = Math.abs(b) / Math.sqrt(2 * (r + a));
    556       }
    557 
    558       return new Complex(re, b < 0 ? -im : im);
    559     },
    560 
    561     /**
    562      * Calculate the complex exponent
    563      *
    564      * @returns {Complex}
    565      */
    566     'exp': function() {
    567 
    568       var tmp = Math.exp(this['re']);
    569 
    570       if (this['im'] === 0) {
    571         //return new Complex(tmp, 0);
    572       }
    573       return new Complex(
    574         tmp * Math.cos(this['im']),
    575         tmp * Math.sin(this['im']));
    576     },
    577 
    578     /**
    579      * Calculate the complex exponent and subtracts one.
    580      *
    581      * This may be more accurate than `Complex(x).exp().sub(1)` if
    582      * `x` is small.
    583      *
    584      * @returns {Complex}
    585      */
    586     'expm1': function() {
    587 
    588       /**
    589        * exp(a + i*b) - 1
    590        = exp(a) * (cos(b) + j*sin(b)) - 1
    591        = expm1(a)*cos(b) + cosm1(b) + j*exp(a)*sin(b)
    592        */
    593 
    594       var a = this['re'];
    595       var b = this['im'];
    596 
    597       return new Complex(
    598         Math.expm1(a) * Math.cos(b) + cosm1(b),
    599         Math.exp(a) * Math.sin(b));
    600     },
    601 
    602     /**
    603      * Calculate the natural log
    604      *
    605      * @returns {Complex}
    606      */
    607     'log': function() {
    608 
    609       var a = this['re'];
    610       var b = this['im'];
    611 
    612       if (b === 0 && a > 0) {
    613         //return new Complex(Math.log(a), 0);
    614       }
    615 
    616       return new Complex(
    617         logHypot(a, b),
    618         Math.atan2(b, a));
    619     },
    620 
    621     /**
    622      * Calculate the magnitude of the complex number
    623      *
    624      * @returns {number}
    625      */
    626     'abs': function() {
    627 
    628       return hypot(this['re'], this['im']);
    629     },
    630 
    631     /**
    632      * Calculate the angle of the complex number
    633      *
    634      * @returns {number}
    635      */
    636     'arg': function() {
    637 
    638       return Math.atan2(this['im'], this['re']);
    639     },
    640 
    641     /**
    642      * Calculate the sine of the complex number
    643      *
    644      * @returns {Complex}
    645      */
    646     'sin': function() {
    647 
    648       // sin(z) = ( e^iz - e^-iz ) / 2i 
    649       //        = sin(a)cosh(b) + i cos(a)sinh(b)
    650 
    651       var a = this['re'];
    652       var b = this['im'];
    653 
    654       return new Complex(
    655         Math.sin(a) * cosh(b),
    656         Math.cos(a) * sinh(b));
    657     },
    658 
    659     /**
    660      * Calculate the cosine
    661      *
    662      * @returns {Complex}
    663      */
    664     'cos': function() {
    665 
    666       // cos(z) = ( e^iz + e^-iz ) / 2 
    667       //        = cos(a)cosh(b) - i sin(a)sinh(b)
    668 
    669       var a = this['re'];
    670       var b = this['im'];
    671 
    672       return new Complex(
    673         Math.cos(a) * cosh(b),
    674         -Math.sin(a) * sinh(b));
    675     },
    676 
    677     /**
    678      * Calculate the tangent
    679      *
    680      * @returns {Complex}
    681      */
    682     'tan': function() {
    683 
    684       // tan(z) = sin(z) / cos(z) 
    685       //        = ( e^iz - e^-iz ) / ( i( e^iz + e^-iz ) )
    686       //        = ( e^2iz - 1 ) / i( e^2iz + 1 )
    687       //        = ( sin(2a) + i sinh(2b) ) / ( cos(2a) + cosh(2b) )
    688 
    689       var a = 2 * this['re'];
    690       var b = 2 * this['im'];
    691       var d = Math.cos(a) + cosh(b);
    692 
    693       return new Complex(
    694         Math.sin(a) / d,
    695         sinh(b) / d);
    696     },
    697 
    698     /**
    699      * Calculate the cotangent
    700      *
    701      * @returns {Complex}
    702      */
    703     'cot': function() {
    704 
    705       // cot(c) = i(e^(ci) + e^(-ci)) / (e^(ci) - e^(-ci))
    706 
    707       var a = 2 * this['re'];
    708       var b = 2 * this['im'];
    709       var d = Math.cos(a) - cosh(b);
    710 
    711       return new Complex(
    712         -Math.sin(a) / d,
    713         sinh(b) / d);
    714     },
    715 
    716     /**
    717      * Calculate the secant
    718      *
    719      * @returns {Complex}
    720      */
    721     'sec': function() {
    722 
    723       // sec(c) = 2 / (e^(ci) + e^(-ci))
    724 
    725       var a = this['re'];
    726       var b = this['im'];
    727       var d = 0.5 * cosh(2 * b) + 0.5 * Math.cos(2 * a);
    728 
    729       return new Complex(
    730         Math.cos(a) * cosh(b) / d,
    731         Math.sin(a) * sinh(b) / d);
    732     },
    733 
    734     /**
    735      * Calculate the cosecans
    736      *
    737      * @returns {Complex}
    738      */
    739     'csc': function() {
    740 
    741       // csc(c) = 2i / (e^(ci) - e^(-ci))
    742 
    743       var a = this['re'];
    744       var b = this['im'];
    745       var d = 0.5 * cosh(2 * b) - 0.5 * Math.cos(2 * a);
    746 
    747       return new Complex(
    748         Math.sin(a) * cosh(b) / d,
    749         -Math.cos(a) * sinh(b) / d);
    750     },
    751 
    752     /**
    753      * Calculate the complex arcus sinus
    754      *
    755      * @returns {Complex}
    756      */
    757     'asin': function() {
    758 
    759       // asin(c) = -i * log(ci + sqrt(1 - c^2))
    760 
    761       var a = this['re'];
    762       var b = this['im'];
    763 
    764       var t1 = new Complex(
    765         b * b - a * a + 1,
    766         -2 * a * b)['sqrt']();
    767 
    768       var t2 = new Complex(
    769         t1['re'] - b,
    770         t1['im'] + a)['log']();
    771 
    772       return new Complex(t2['im'], -t2['re']);
    773     },
    774 
    775     /**
    776      * Calculate the complex arcus cosinus
    777      *
    778      * @returns {Complex}
    779      */
    780     'acos': function() {
    781 
    782       // acos(c) = i * log(c - i * sqrt(1 - c^2))
    783 
    784       var a = this['re'];
    785       var b = this['im'];
    786 
    787       var t1 = new Complex(
    788         b * b - a * a + 1,
    789         -2 * a * b)['sqrt']();
    790 
    791       var t2 = new Complex(
    792         t1['re'] - b,
    793         t1['im'] + a)['log']();
    794 
    795       return new Complex(Math.PI / 2 - t2['im'], t2['re']);
    796     },
    797 
    798     /**
    799      * Calculate the complex arcus tangent
    800      *
    801      * @returns {Complex}
    802      */
    803     'atan': function() {
    804 
    805       // atan(c) = i / 2 log((i + x) / (i - x))
    806 
    807       var a = this['re'];
    808       var b = this['im'];
    809 
    810       if (a === 0) {
    811 
    812         if (b === 1) {
    813           return new Complex(0, Infinity);
    814         }
    815 
    816         if (b === -1) {
    817           return new Complex(0, -Infinity);
    818         }
    819       }
    820 
    821       var d = a * a + (1.0 - b) * (1.0 - b);
    822 
    823       var t1 = new Complex(
    824         (1 - b * b - a * a) / d,
    825         -2 * a / d).log();
    826 
    827       return new Complex(-0.5 * t1['im'], 0.5 * t1['re']);
    828     },
    829 
    830     /**
    831      * Calculate the complex arcus cotangent
    832      *
    833      * @returns {Complex}
    834      */
    835     'acot': function() {
    836 
    837       // acot(c) = i / 2 log((c - i) / (c + i))
    838 
    839       var a = this['re'];
    840       var b = this['im'];
    841 
    842       if (b === 0) {
    843         return new Complex(Math.atan2(1, a), 0);
    844       }
    845 
    846       var d = a * a + b * b;
    847       return (d !== 0)
    848         ? new Complex(
    849           a / d,
    850           -b / d).atan()
    851         : new Complex(
    852           (a !== 0) ? a / 0 : 0,
    853           (b !== 0) ? -b / 0 : 0).atan();
    854     },
    855 
    856     /**
    857      * Calculate the complex arcus secant
    858      *
    859      * @returns {Complex}
    860      */
    861     'asec': function() {
    862 
    863       // asec(c) = -i * log(1 / c + sqrt(1 - i / c^2))
    864 
    865       var a = this['re'];
    866       var b = this['im'];
    867 
    868       if (a === 0 && b === 0) {
    869         return new Complex(0, Infinity);
    870       }
    871 
    872       var d = a * a + b * b;
    873       return (d !== 0)
    874         ? new Complex(
    875           a / d,
    876           -b / d).acos()
    877         : new Complex(
    878           (a !== 0) ? a / 0 : 0,
    879           (b !== 0) ? -b / 0 : 0).acos();
    880     },
    881 
    882     /**
    883      * Calculate the complex arcus cosecans
    884      *
    885      * @returns {Complex}
    886      */
    887     'acsc': function() {
    888 
    889       // acsc(c) = -i * log(i / c + sqrt(1 - 1 / c^2))
    890 
    891       var a = this['re'];
    892       var b = this['im'];
    893 
    894       if (a === 0 && b === 0) {
    895         return new Complex(Math.PI / 2, Infinity);
    896       }
    897 
    898       var d = a * a + b * b;
    899       return (d !== 0)
    900         ? new Complex(
    901           a / d,
    902           -b / d).asin()
    903         : new Complex(
    904           (a !== 0) ? a / 0 : 0,
    905           (b !== 0) ? -b / 0 : 0).asin();
    906     },
    907 
    908     /**
    909      * Calculate the complex sinh
    910      *
    911      * @returns {Complex}
    912      */
    913     'sinh': function() {
    914 
    915       // sinh(c) = (e^c - e^-c) / 2
    916 
    917       var a = this['re'];
    918       var b = this['im'];
    919 
    920       return new Complex(
    921         sinh(a) * Math.cos(b),
    922         cosh(a) * Math.sin(b));
    923     },
    924 
    925     /**
    926      * Calculate the complex cosh
    927      *
    928      * @returns {Complex}
    929      */
    930     'cosh': function() {
    931 
    932       // cosh(c) = (e^c + e^-c) / 2
    933 
    934       var a = this['re'];
    935       var b = this['im'];
    936 
    937       return new Complex(
    938         cosh(a) * Math.cos(b),
    939         sinh(a) * Math.sin(b));
    940     },
    941 
    942     /**
    943      * Calculate the complex tanh
    944      *
    945      * @returns {Complex}
    946      */
    947     'tanh': function() {
    948 
    949       // tanh(c) = (e^c - e^-c) / (e^c + e^-c)
    950 
    951       var a = 2 * this['re'];
    952       var b = 2 * this['im'];
    953       var d = cosh(a) + Math.cos(b);
    954 
    955       return new Complex(
    956         sinh(a) / d,
    957         Math.sin(b) / d);
    958     },
    959 
    960     /**
    961      * Calculate the complex coth
    962      *
    963      * @returns {Complex}
    964      */
    965     'coth': function() {
    966 
    967       // coth(c) = (e^c + e^-c) / (e^c - e^-c)
    968 
    969       var a = 2 * this['re'];
    970       var b = 2 * this['im'];
    971       var d = cosh(a) - Math.cos(b);
    972 
    973       return new Complex(
    974         sinh(a) / d,
    975         -Math.sin(b) / d);
    976     },
    977 
    978     /**
    979      * Calculate the complex coth
    980      *
    981      * @returns {Complex}
    982      */
    983     'csch': function() {
    984 
    985       // csch(c) = 2 / (e^c - e^-c)
    986 
    987       var a = this['re'];
    988       var b = this['im'];
    989       var d = Math.cos(2 * b) - cosh(2 * a);
    990 
    991       return new Complex(
    992         -2 * sinh(a) * Math.cos(b) / d,
    993         2 * cosh(a) * Math.sin(b) / d);
    994     },
    995 
    996     /**
    997      * Calculate the complex sech
    998      *
    999      * @returns {Complex}
   1000      */
   1001     'sech': function() {
   1002 
   1003       // sech(c) = 2 / (e^c + e^-c)
   1004 
   1005       var a = this['re'];
   1006       var b = this['im'];
   1007       var d = Math.cos(2 * b) + cosh(2 * a);
   1008 
   1009       return new Complex(
   1010         2 * cosh(a) * Math.cos(b) / d,
   1011         -2 * sinh(a) * Math.sin(b) / d);
   1012     },
   1013 
   1014     /**
   1015      * Calculate the complex asinh
   1016      *
   1017      * @returns {Complex}
   1018      */
   1019     'asinh': function() {
   1020 
   1021       // asinh(c) = log(c + sqrt(c^2 + 1))
   1022 
   1023       var tmp = this['im'];
   1024       this['im'] = -this['re'];
   1025       this['re'] = tmp;
   1026       var res = this['asin']();
   1027 
   1028       this['re'] = -this['im'];
   1029       this['im'] = tmp;
   1030       tmp = res['re'];
   1031 
   1032       res['re'] = -res['im'];
   1033       res['im'] = tmp;
   1034       return res;
   1035     },
   1036 
   1037     /**
   1038      * Calculate the complex acosh
   1039      *
   1040      * @returns {Complex}
   1041      */
   1042     'acosh': function() {
   1043 
   1044       // acosh(c) = log(c + sqrt(c^2 - 1))
   1045 
   1046       var res = this['acos']();
   1047       if (res['im'] <= 0) {
   1048         var tmp = res['re'];
   1049         res['re'] = -res['im'];
   1050         res['im'] = tmp;
   1051       } else {
   1052         var tmp = res['im'];
   1053         res['im'] = -res['re'];
   1054         res['re'] = tmp;
   1055       }
   1056       return res;
   1057     },
   1058 
   1059     /**
   1060      * Calculate the complex atanh
   1061      *
   1062      * @returns {Complex}
   1063      */
   1064     'atanh': function() {
   1065 
   1066       // atanh(c) = log((1+c) / (1-c)) / 2
   1067 
   1068       var a = this['re'];
   1069       var b = this['im'];
   1070 
   1071       var noIM = a > 1 && b === 0;
   1072       var oneMinus = 1 - a;
   1073       var onePlus = 1 + a;
   1074       var d = oneMinus * oneMinus + b * b;
   1075 
   1076       var x = (d !== 0)
   1077         ? new Complex(
   1078           (onePlus * oneMinus - b * b) / d,
   1079           (b * oneMinus + onePlus * b) / d)
   1080         : new Complex(
   1081           (a !== -1) ? (a / 0) : 0,
   1082           (b !== 0) ? (b / 0) : 0);
   1083 
   1084       var temp = x['re'];
   1085       x['re'] = logHypot(x['re'], x['im']) / 2;
   1086       x['im'] = Math.atan2(x['im'], temp) / 2;
   1087       if (noIM) {
   1088         x['im'] = -x['im'];
   1089       }
   1090       return x;
   1091     },
   1092 
   1093     /**
   1094      * Calculate the complex acoth
   1095      *
   1096      * @returns {Complex}
   1097      */
   1098     'acoth': function() {
   1099 
   1100       // acoth(c) = log((c+1) / (c-1)) / 2
   1101 
   1102       var a = this['re'];
   1103       var b = this['im'];
   1104 
   1105       if (a === 0 && b === 0) {
   1106         return new Complex(0, Math.PI / 2);
   1107       }
   1108 
   1109       var d = a * a + b * b;
   1110       return (d !== 0)
   1111         ? new Complex(
   1112           a / d,
   1113           -b / d).atanh()
   1114         : new Complex(
   1115           (a !== 0) ? a / 0 : 0,
   1116           (b !== 0) ? -b / 0 : 0).atanh();
   1117     },
   1118 
   1119     /**
   1120      * Calculate the complex acsch
   1121      *
   1122      * @returns {Complex}
   1123      */
   1124     'acsch': function() {
   1125 
   1126       // acsch(c) = log((1+sqrt(1+c^2))/c)
   1127 
   1128       var a = this['re'];
   1129       var b = this['im'];
   1130 
   1131       if (b === 0) {
   1132 
   1133         return new Complex(
   1134           (a !== 0)
   1135             ? Math.log(a + Math.sqrt(a * a + 1))
   1136             : Infinity, 0);
   1137       }
   1138 
   1139       var d = a * a + b * b;
   1140       return (d !== 0)
   1141         ? new Complex(
   1142           a / d,
   1143           -b / d).asinh()
   1144         : new Complex(
   1145           (a !== 0) ? a / 0 : 0,
   1146           (b !== 0) ? -b / 0 : 0).asinh();
   1147     },
   1148 
   1149     /**
   1150      * Calculate the complex asech
   1151      *
   1152      * @returns {Complex}
   1153      */
   1154     'asech': function() {
   1155 
   1156       // asech(c) = log((1+sqrt(1-c^2))/c)
   1157 
   1158       var a = this['re'];
   1159       var b = this['im'];
   1160 
   1161       if (this['isZero']()) {
   1162         return Complex['INFINITY'];
   1163       }
   1164 
   1165       var d = a * a + b * b;
   1166       return (d !== 0)
   1167         ? new Complex(
   1168           a / d,
   1169           -b / d).acosh()
   1170         : new Complex(
   1171           (a !== 0) ? a / 0 : 0,
   1172           (b !== 0) ? -b / 0 : 0).acosh();
   1173     },
   1174 
   1175     /**
   1176      * Calculate the complex inverse 1/z
   1177      *
   1178      * @returns {Complex}
   1179      */
   1180     'inverse': function() {
   1181 
   1182       // 1 / 0 = Infinity and 1 / Infinity = 0
   1183       if (this['isZero']()) {
   1184         return Complex['INFINITY'];
   1185       }
   1186 
   1187       if (this['isInfinite']()) {
   1188         return Complex['ZERO'];
   1189       }
   1190 
   1191       var a = this['re'];
   1192       var b = this['im'];
   1193 
   1194       var d = a * a + b * b;
   1195 
   1196       return new Complex(a / d, -b / d);
   1197     },
   1198 
   1199     /**
   1200      * Returns the complex conjugate
   1201      *
   1202      * @returns {Complex}
   1203      */
   1204     'conjugate': function() {
   1205 
   1206       return new Complex(this['re'], -this['im']);
   1207     },
   1208 
   1209     /**
   1210      * Gets the negated complex number
   1211      *
   1212      * @returns {Complex}
   1213      */
   1214     'neg': function() {
   1215 
   1216       return new Complex(-this['re'], -this['im']);
   1217     },
   1218 
   1219     /**
   1220      * Ceils the actual complex number
   1221      *
   1222      * @returns {Complex}
   1223      */
   1224     'ceil': function(places) {
   1225 
   1226       places = Math.pow(10, places || 0);
   1227 
   1228       return new Complex(
   1229         Math.ceil(this['re'] * places) / places,
   1230         Math.ceil(this['im'] * places) / places);
   1231     },
   1232 
   1233     /**
   1234      * Floors the actual complex number
   1235      *
   1236      * @returns {Complex}
   1237      */
   1238     'floor': function(places) {
   1239 
   1240       places = Math.pow(10, places || 0);
   1241 
   1242       return new Complex(
   1243         Math.floor(this['re'] * places) / places,
   1244         Math.floor(this['im'] * places) / places);
   1245     },
   1246 
   1247     /**
   1248      * Ceils the actual complex number
   1249      *
   1250      * @returns {Complex}
   1251      */
   1252     'round': function(places) {
   1253 
   1254       places = Math.pow(10, places || 0);
   1255 
   1256       return new Complex(
   1257         Math.round(this['re'] * places) / places,
   1258         Math.round(this['im'] * places) / places);
   1259     },
   1260 
   1261     /**
   1262      * Compares two complex numbers
   1263      *
   1264      * **Note:** new Complex(Infinity).equals(Infinity) === false
   1265      *
   1266      * @returns {boolean}
   1267      */
   1268     'equals': function(a, b) {
   1269 
   1270       var z = new Complex(a, b);
   1271 
   1272       return Math.abs(z['re'] - this['re']) <= Complex['EPSILON'] &&
   1273         Math.abs(z['im'] - this['im']) <= Complex['EPSILON'];
   1274     },
   1275 
   1276     /**
   1277      * Clones the actual object
   1278      *
   1279      * @returns {Complex}
   1280      */
   1281     'clone': function() {
   1282 
   1283       return new Complex(this['re'], this['im']);
   1284     },
   1285 
   1286     /**
   1287      * Gets a string of the actual complex number
   1288      *
   1289      * @returns {string}
   1290      */
   1291     'toString': function() {
   1292 
   1293       var a = this['re'];
   1294       var b = this['im'];
   1295       var ret = "";
   1296 
   1297       if (this['isNaN']()) {
   1298         return 'NaN';
   1299       }
   1300 
   1301       if (this['isInfinite']()) {
   1302         return 'Infinity';
   1303       }
   1304 
   1305       if (Math.abs(a) < Complex['EPSILON']) {
   1306         a = 0;
   1307       }
   1308 
   1309       if (Math.abs(b) < Complex['EPSILON']) {
   1310         b = 0;
   1311       }
   1312 
   1313       // If is real number
   1314       if (b === 0) {
   1315         return ret + a;
   1316       }
   1317 
   1318       if (a !== 0) {
   1319         ret += a;
   1320         ret += " ";
   1321         if (b < 0) {
   1322           b = -b;
   1323           ret += "-";
   1324         } else {
   1325           ret += "+";
   1326         }
   1327         ret += " ";
   1328       } else if (b < 0) {
   1329         b = -b;
   1330         ret += "-";
   1331       }
   1332 
   1333       if (1 !== b) { // b is the absolute imaginary part
   1334         ret += b;
   1335       }
   1336       return ret + "i";
   1337     },
   1338 
   1339     /**
   1340      * Returns the actual number as a vector
   1341      *
   1342      * @returns {Array}
   1343      */
   1344     'toVector': function() {
   1345 
   1346       return [this['re'], this['im']];
   1347     },
   1348 
   1349     /**
   1350      * Returns the actual real value of the current object
   1351      *
   1352      * @returns {number|null}
   1353      */
   1354     'valueOf': function() {
   1355 
   1356       if (this['im'] === 0) {
   1357         return this['re'];
   1358       }
   1359       return null;
   1360     },
   1361 
   1362     /**
   1363      * Determines whether a complex number is not on the Riemann sphere.
   1364      *
   1365      * @returns {boolean}
   1366      */
   1367     'isNaN': function() {
   1368       return isNaN(this['re']) || isNaN(this['im']);
   1369     },
   1370 
   1371     /**
   1372      * Determines whether or not a complex number is at the zero pole of the
   1373      * Riemann sphere.
   1374      *
   1375      * @returns {boolean}
   1376      */
   1377     'isZero': function() {
   1378       return this['im'] === 0 && this['re'] === 0;
   1379     },
   1380 
   1381     /**
   1382      * Determines whether a complex number is not at the infinity pole of the
   1383      * Riemann sphere.
   1384      *
   1385      * @returns {boolean}
   1386      */
   1387     'isFinite': function() {
   1388       return isFinite(this['re']) && isFinite(this['im']);
   1389     },
   1390 
   1391     /**
   1392      * Determines whether or not a complex number is at the infinity pole of the
   1393      * Riemann sphere.
   1394      *
   1395      * @returns {boolean}
   1396      */
   1397     'isInfinite': function() {
   1398       return !(this['isNaN']() || this['isFinite']());
   1399     }
   1400   };
   1401 
   1402   Complex['ZERO'] = new Complex(0, 0);
   1403   Complex['ONE'] = new Complex(1, 0);
   1404   Complex['I'] = new Complex(0, 1);
   1405   Complex['PI'] = new Complex(Math.PI, 0);
   1406   Complex['E'] = new Complex(Math.E, 0);
   1407   Complex['INFINITY'] = new Complex(Infinity, Infinity);
   1408   Complex['NAN'] = new Complex(NaN, NaN);
   1409   Complex['EPSILON'] = 1e-15;
   1410 
   1411   if (typeof define === 'function' && define['amd']) {
   1412     define([], function() {
   1413       return Complex;
   1414     });
   1415   } else if (typeof exports === 'object') {
   1416     Object.defineProperty(Complex, "__esModule", { 'value': true });
   1417     Complex['default'] = Complex;
   1418     Complex['Complex'] = Complex;
   1419     module['exports'] = Complex;
   1420   } else {
   1421     root['Complex'] = Complex;
   1422   }
   1423 
   1424 })(this);