1184 lines
24 KiB
JavaScript
1184 lines
24 KiB
JavaScript
/**
|
|
* @license Complex.js v2.0.1 11/02/2016
|
|
*
|
|
* Copyright (c) 2016, Robert Eisele (robert@xarg.org)
|
|
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
**/
|
|
|
|
/**
|
|
*
|
|
* This class allows the manipilation of complex numbers.
|
|
* You can pass a complex number in different formats. Either as object, double, string or two integer parameters.
|
|
*
|
|
* Object form
|
|
* { re: <real>, im: <imaginary> }
|
|
* { arg: <angle>, abs: <radius> }
|
|
* { phi: <angle>, r: <radius> }
|
|
*
|
|
* Double form
|
|
* 99.3 - Single double value
|
|
*
|
|
* String form
|
|
* "23.1337" - Simple real number
|
|
* "15+3i" - a simple complex number
|
|
* "3-i" - a simple complex number
|
|
*
|
|
* Example:
|
|
*
|
|
* var c = new Complex("99.3+8i");
|
|
* c.mul({r: 3, i: 9}).div(4.9).sub(3, 2);
|
|
*
|
|
*/
|
|
|
|
(function(root) {
|
|
|
|
"use strict";
|
|
|
|
var P = {'re': 0, 'im': 0};
|
|
|
|
Math.cosh = Math.cosh || function(x) {
|
|
return (Math.exp(x) + Math.exp(-x)) * 0.5;
|
|
};
|
|
|
|
Math.sinh = Math.sinh || function(x) {
|
|
return (Math.exp(x) - Math.exp(-x)) * 0.5;
|
|
};
|
|
|
|
var parser_exit = function() {
|
|
throw SyntaxError("Invalid Param");
|
|
};
|
|
|
|
/**
|
|
* Calculates log(sqrt(a^2+b^2)) in a way to avoid overflows
|
|
*
|
|
* @param {number} a
|
|
* @param {number} b
|
|
* @returns {number}
|
|
*/
|
|
function logHypot(a, b) {
|
|
|
|
var _a = Math.abs(a);
|
|
var _b = Math.abs(b);
|
|
|
|
if (a === 0) {
|
|
return Math.log(_b);
|
|
}
|
|
|
|
if (b === 0) {
|
|
return Math.log(_a);
|
|
}
|
|
|
|
if (_a < 3000 && _b < 3000) {
|
|
return Math.log(a * a + b * b) * 0.5;
|
|
}
|
|
|
|
/* I got 4 ideas to compute this property without overflow:
|
|
*
|
|
* Testing 1000000 times with random samples for a,b ∈ [1, 1000000000] against a big decimal library to get an error estimate
|
|
*
|
|
* 1. Only eliminate the square root: (OVERALL ERROR: 3.9122483030951116e-11)
|
|
|
|
Math.log(a * a + b * b) / 2
|
|
|
|
*
|
|
*
|
|
* 2. Try to use the non-overflowing pythagoras: (OVERALL ERROR: 8.889760039210159e-10)
|
|
|
|
var fn = function(a, b) {
|
|
a = Math.abs(a);
|
|
b = Math.abs(b);
|
|
var t = Math.min(a, b);
|
|
a = Math.max(a, b);
|
|
t = t / a;
|
|
|
|
return Math.log(a) + Math.log(1 + t * t) / 2;
|
|
};
|
|
|
|
* 3. Abuse the identity cos(atan(y/x) = x / sqrt(x^2+y^2): (OVERALL ERROR: 3.4780178737037204e-10)
|
|
|
|
Math.log(a / Math.cos(Math.atan2(b, a)))
|
|
|
|
* 4. Use 3. and apply log rules: (OVERALL ERROR: 1.2014087502620896e-9)
|
|
|
|
Math.log(a) - Math.log(Math.cos(Math.atan2(b, a)))
|
|
|
|
*/
|
|
|
|
return Math.log(a / Math.cos(Math.atan2(b, a)));
|
|
}
|
|
|
|
var parse = function(a, b) {
|
|
|
|
if (a === undefined || a === null) {
|
|
P["re"] =
|
|
P["im"] = 0;
|
|
} else if (b !== undefined) {
|
|
P["re"] = a;
|
|
P["im"] = b;
|
|
} else switch (typeof a) {
|
|
|
|
case "object":
|
|
|
|
if ("im" in a && "re" in a) {
|
|
P["re"] = a["re"];
|
|
P["im"] = a["im"];
|
|
} else if ("abs" in a && "arg" in a) {
|
|
P["re"] = a["abs"] * Math.cos(a["arg"]);
|
|
P["im"] = a["abs"] * Math.sin(a["arg"]);
|
|
} else if ("r" in a && "phi" in a) {
|
|
P["re"] = a["r"] * Math.cos(a["phi"]);
|
|
P["im"] = a["r"] * Math.sin(a["phi"]);
|
|
} else {
|
|
parser_exit();
|
|
}
|
|
break;
|
|
|
|
case "string":
|
|
|
|
P["im"] = /* void */
|
|
P["re"] = 0;
|
|
|
|
var tokens = a.match(/\d+\.?\d*e[+-]?\d+|\d+\.?\d*|\.\d+|./g);
|
|
var plus = 1;
|
|
var minus = 0;
|
|
|
|
if (tokens === null) {
|
|
parser_exit();
|
|
}
|
|
|
|
for (var i = 0; i < tokens.length; i++) {
|
|
|
|
var c = tokens[i];
|
|
|
|
if (c === ' ' || c === '\t' || c === '\n') {
|
|
/* void */
|
|
} else if (c === '+') {
|
|
plus++;
|
|
} else if (c === '-') {
|
|
minus++;
|
|
} else if (c === 'i' || c === 'I') {
|
|
|
|
if (plus + minus === 0) {
|
|
parser_exit();
|
|
}
|
|
|
|
if (tokens[i + 1] !== ' ' && !isNaN(tokens[i + 1])) {
|
|
P["im"]+= parseFloat((minus % 2 ? "-" : "") + tokens[i + 1]);
|
|
i++;
|
|
} else {
|
|
P["im"]+= parseFloat((minus % 2 ? "-" : "") + "1");
|
|
}
|
|
plus = minus = 0;
|
|
|
|
} else {
|
|
|
|
if (plus + minus === 0 || isNaN(c)) {
|
|
parser_exit();
|
|
}
|
|
|
|
if (tokens[i + 1] === 'i' || tokens[i + 1] === 'I') {
|
|
P["im"]+= parseFloat((minus % 2 ? "-" : "") + c);
|
|
i++;
|
|
} else {
|
|
P["re"]+= parseFloat((minus % 2 ? "-" : "") + c);
|
|
}
|
|
plus = minus = 0;
|
|
}
|
|
}
|
|
|
|
// Still something on the stack
|
|
if (plus + minus > 0) {
|
|
parser_exit();
|
|
}
|
|
break;
|
|
|
|
case "number":
|
|
P["im"] = 0;
|
|
P["re"] = a;
|
|
break;
|
|
|
|
default:
|
|
parser_exit();
|
|
}
|
|
|
|
if (isNaN(P["re"]) || isNaN(P["im"])) {
|
|
// If a calculation is NaN, we treat it as NaN and don't throw
|
|
//parser_exit();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @constructor
|
|
* @returns {Complex}
|
|
*/
|
|
function Complex(a, b) {
|
|
|
|
if (!(this instanceof Complex)) {
|
|
return new Complex(a, b);
|
|
}
|
|
|
|
parse(a, b); // mutates P
|
|
|
|
this["re"] = P["re"];
|
|
this["im"] = P["im"];
|
|
}
|
|
|
|
Complex.prototype = {
|
|
|
|
"re": 0,
|
|
"im": 0,
|
|
|
|
/**
|
|
* Calculates the sign of a complex number
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"sign": function() {
|
|
|
|
var abs = this["abs"]();
|
|
|
|
return new Complex(
|
|
this["re"] / abs,
|
|
this["im"] / abs);
|
|
},
|
|
|
|
/**
|
|
* Adds two complex numbers
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"add": function(a, b) {
|
|
|
|
parse(a, b); // mutates P
|
|
|
|
return new Complex(
|
|
this["re"] + P["re"],
|
|
this["im"] + P["im"]);
|
|
},
|
|
|
|
/**
|
|
* Subtracts two complex numbers
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"sub": function(a, b) {
|
|
|
|
parse(a, b); // mutates P
|
|
|
|
return new Complex(
|
|
this["re"] - P["re"],
|
|
this["im"] - P["im"]);
|
|
},
|
|
|
|
/**
|
|
* Multiplies two complex numbers
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"mul": function(a, b) {
|
|
|
|
parse(a, b); // mutates P
|
|
|
|
// Besides the addition/subtraction, this helps having a solution for rational Infinity
|
|
if (P['im'] === 0 && this['im'] === 0) {
|
|
return new Complex(this['re'] * P['re'], 0);
|
|
}
|
|
|
|
return new Complex(
|
|
this["re"] * P["re"] - this["im"] * P["im"],
|
|
this["re"] * P["im"] + this["im"] * P["re"]);
|
|
},
|
|
|
|
/**
|
|
* Divides two complex numbers
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"div": function(a, b) {
|
|
|
|
parse(a, b); // mutates P
|
|
|
|
a = this["re"];
|
|
b = this["im"];
|
|
|
|
var c = P["re"];
|
|
var d = P["im"];
|
|
var t, x;
|
|
|
|
// Divisor is zero
|
|
if (0 === c && 0 === d) {
|
|
return new Complex(
|
|
(a !== 0) ? (a / 0) : 0,
|
|
(b !== 0) ? (b / 0) : 0);
|
|
}
|
|
|
|
// Divisor is rational
|
|
if (0 === d) {
|
|
return new Complex(a / c, b / c);
|
|
}
|
|
|
|
if (Math.abs(c) < Math.abs(d)) {
|
|
|
|
x = c / d;
|
|
t = c * x + d;
|
|
|
|
return new Complex(
|
|
(a * x + b) / t,
|
|
(b * x - a) / t);
|
|
|
|
} else {
|
|
|
|
x = d / c;
|
|
t = d * x + c;
|
|
|
|
return new Complex(
|
|
(a + b * x) / t,
|
|
(b - a * x) / t);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Calculate the power of two complex numbers
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"pow": function(a, b) {
|
|
|
|
parse(a, b); // mutates P
|
|
|
|
a = this["re"];
|
|
b = this["im"];
|
|
|
|
if (a === 0 && b === 0) {
|
|
return new Complex(0, 0);
|
|
}
|
|
|
|
var arg = Math.atan2(b, a);
|
|
var loh = logHypot(a, b);
|
|
|
|
if (P["im"] === 0) {
|
|
|
|
if (b === 0 && a >= 0) {
|
|
|
|
return new Complex(Math.pow(a, P["re"]), 0);
|
|
|
|
} else if (a === 0) {
|
|
|
|
switch (P["re"] % 4) {
|
|
case 0:
|
|
return new Complex(Math.pow(b, P["re"]), 0);
|
|
case 1:
|
|
return new Complex(0, Math.pow(b, P["re"]));
|
|
case 2:
|
|
return new Complex(-Math.pow(b, P["re"]), 0);
|
|
case 3:
|
|
return new Complex(0, -Math.pow(b, P["re"]));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* I couldn"t find a good formula, so here is a derivation and optimization
|
|
*
|
|
* z_1^z_2 = (a + bi)^(c + di)
|
|
* = exp((c + di) * log(a + bi)
|
|
* = pow(a^2 + b^2, (c + di) / 2) * exp(i(c + di)atan2(b, a))
|
|
* =>...
|
|
* 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))
|
|
* 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))
|
|
*
|
|
* =>...
|
|
* 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))
|
|
* 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))
|
|
*
|
|
* =>
|
|
* Re = exp(c * logsq2 - d * arg(z_1)) * cos(d * logsq2 + c * arg(z_1))
|
|
* Im = exp(c * logsq2 - d * arg(z_1)) * sin(d * logsq2 + c * arg(z_1))
|
|
*
|
|
*/
|
|
|
|
a = Math.exp(P["re"] * loh - P["im"] * arg);
|
|
b = P["im"] * loh + P["re"] * arg;
|
|
return new Complex(
|
|
a * Math.cos(b),
|
|
a * Math.sin(b));
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex square root
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"sqrt": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
var r = this["abs"]();
|
|
|
|
var re, im;
|
|
|
|
if (a >= 0 && b === 0) {
|
|
return new Complex(Math.sqrt(a), 0);
|
|
}
|
|
|
|
if (a >= 0) {
|
|
re = 0.5 * Math.sqrt(2.0 * (r + a));
|
|
} else {
|
|
re = Math.abs(b) / Math.sqrt(2 * (r - a));
|
|
}
|
|
|
|
if (a <= 0) {
|
|
im = 0.5 * Math.sqrt(2.0 * (r - a));
|
|
} else {
|
|
im = Math.abs(b) / Math.sqrt(2 * (r + a));
|
|
}
|
|
|
|
return new Complex(re, b >= 0 ? im : -im);
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex exponent
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"exp": function() {
|
|
|
|
var tmp = Math.exp(this["re"]);
|
|
|
|
if (this["im"] === 0) {
|
|
//return new Complex(tmp, 0);
|
|
}
|
|
return new Complex(
|
|
tmp * Math.cos(this["im"]),
|
|
tmp * Math.sin(this["im"]));
|
|
},
|
|
|
|
/**
|
|
* Calculate the natural log
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"log": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
if (b === 0 && a > 0) {
|
|
//return new Complex(Math.log(a), 0);
|
|
}
|
|
|
|
return new Complex(
|
|
logHypot(a, b),
|
|
Math.atan2(b, a));
|
|
},
|
|
|
|
/**
|
|
* Calculate the magniture of the complex number
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
"abs": function() {
|
|
|
|
var a = Math.abs(this["re"]);
|
|
var b = Math.abs(this["im"]);
|
|
|
|
if (a < 3000 && b < 3000) {
|
|
return Math.sqrt(a * a + b * b);
|
|
}
|
|
|
|
if (a < b) {
|
|
a = b;
|
|
b = this["re"] / this["im"];
|
|
} else {
|
|
b = this["im"] / this["re"];
|
|
}
|
|
return a * Math.sqrt(1 + b * b);
|
|
},
|
|
|
|
/**
|
|
* Calculate the angle of the complex number
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
"arg": function() {
|
|
|
|
return Math.atan2(this["im"], this["re"]);
|
|
},
|
|
|
|
/**
|
|
* Calculate the sine of the complex number
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"sin": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
return new Complex(
|
|
Math.sin(a) * Math.cosh(b),
|
|
Math.cos(a) * Math.sinh(b));
|
|
},
|
|
|
|
/**
|
|
* Calculate the cosine
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"cos": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
return new Complex(
|
|
Math.cos(a) * Math.cosh(b),
|
|
-Math.sin(a) * Math.sinh(b));
|
|
},
|
|
|
|
/**
|
|
* Calculate the tangent
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"tan": function() {
|
|
|
|
var a = 2 * this["re"];
|
|
var b = 2 * this["im"];
|
|
var d = Math.cos(a) + Math.cosh(b);
|
|
|
|
return new Complex(
|
|
Math.sin(a) / d,
|
|
Math.sinh(b) / d);
|
|
},
|
|
|
|
/**
|
|
* Calculate the cotangent
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"cot": function() {
|
|
|
|
var a = 2 * this["re"];
|
|
var b = 2 * this["im"];
|
|
var d = Math.cos(a) - Math.cosh(b);
|
|
|
|
return new Complex(
|
|
-Math.sin(a) / d,
|
|
Math.sinh(b) / d);
|
|
},
|
|
|
|
/**
|
|
* Calculate the secant
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"sec": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
var d = 0.5 * Math.cosh(2 * b) + 0.5 * Math.cos(2 * a);
|
|
|
|
return new Complex(
|
|
Math.cos(a) * Math.cosh(b) / d,
|
|
Math.sin(a) * Math.sinh(b) / d);
|
|
},
|
|
|
|
/**
|
|
* Calculate the cosecans
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"csc": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
var d = 0.5 * Math.cosh(2 * b) - 0.5 * Math.cos(2 * a);
|
|
|
|
return new Complex(
|
|
Math.sin(a) * Math.cosh(b) / d,
|
|
-Math.cos(a) * Math.sinh(b) / d);
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex arcus sinus
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"asin": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
var t1 = new Complex(
|
|
b * b - a * a + 1,
|
|
-2 * a * b)['sqrt']();
|
|
|
|
var t2 = new Complex(
|
|
t1['re'] - b,
|
|
t1['im'] + a)['log']();
|
|
|
|
return new Complex(t2['im'], -t2['re']);
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex arcus cosinus
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"acos": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
var t1 = new Complex(
|
|
b * b - a * a + 1,
|
|
-2 * a * b)['sqrt']();
|
|
|
|
var t2 = new Complex(
|
|
t1["re"] - b,
|
|
t1["im"] + a)['log']();
|
|
|
|
return new Complex(Math.PI / 2 - t2["im"], t2["re"]);
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex arcus tangent
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"atan": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
if (a === 0) {
|
|
|
|
if (b === 1) {
|
|
return new Complex(0, Infinity);
|
|
}
|
|
|
|
if (b === -1) {
|
|
return new Complex(0, -Infinity);
|
|
}
|
|
}
|
|
|
|
var d = a * a + (1.0 - b) * (1.0 - b);
|
|
|
|
var t1 = new Complex(
|
|
(1 - b * b - a * a) / d,
|
|
-2 * a / d).log();
|
|
|
|
return new Complex(-0.5 * t1["im"], 0.5 * t1["re"]);
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex arcus cotangent
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"acot": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
if (b === 0) {
|
|
return new Complex(Math.atan2(1, a), 0);
|
|
}
|
|
|
|
var d = a * a + b * b;
|
|
return (d !== 0)
|
|
? new Complex(
|
|
a / d,
|
|
-b / d).atan()
|
|
: new Complex(
|
|
(a !== 0) ? a / 0 : 0,
|
|
(b !== 0) ?-b / 0 : 0).atan();
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex arcus secant
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"asec": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
if (a === 0 && b === 0) {
|
|
return new Complex(0, Infinity);
|
|
}
|
|
|
|
var d = a * a + b * b;
|
|
return (d !== 0)
|
|
? new Complex(
|
|
a / d,
|
|
-b / d).acos()
|
|
: new Complex(
|
|
(a !== 0) ? a / 0 : 0,
|
|
(b !== 0) ?-b / 0 : 0).acos();
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex arcus cosecans
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"acsc": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
if (a === 0 && b === 0) {
|
|
return new Complex(Math.PI / 2, Infinity);
|
|
}
|
|
|
|
var d = a * a + b * b;
|
|
return (d !== 0)
|
|
? new Complex(
|
|
a / d,
|
|
-b / d).asin()
|
|
: new Complex(
|
|
(a !== 0) ? a / 0 : 0,
|
|
(b !== 0) ?-b / 0 : 0).asin();
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex sinh
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"sinh": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
return new Complex(
|
|
Math.sinh(a) * Math.cos(b),
|
|
Math.cosh(a) * Math.sin(b));
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex cosh
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"cosh": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
return new Complex(
|
|
Math.cosh(a) * Math.cos(b),
|
|
Math.sinh(a) * Math.sin(b));
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex tanh
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"tanh": function() {
|
|
|
|
var a = 2 * this["re"];
|
|
var b = 2 * this["im"];
|
|
var d = Math.cosh(a) + Math.cos(b);
|
|
|
|
return new Complex(
|
|
Math.sinh(a) / d,
|
|
Math.sin(b) / d);
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex coth
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"coth": function() {
|
|
|
|
var a = 2 * this["re"];
|
|
var b = 2 * this["im"];
|
|
var d = Math.cosh(a) - Math.cos(b);
|
|
|
|
return new Complex(
|
|
Math.sinh(a) / d,
|
|
-Math.sin(b) / d);
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex coth
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"csch": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
var d = Math.cos(2 * b) - Math.cosh(2 * a);
|
|
|
|
return new Complex(
|
|
-2 * Math.sinh(a) * Math.cos(b) / d,
|
|
2 * Math.cosh(a) * Math.sin(b) / d);
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex sech
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"sech": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
var d = Math.cos(2 * b) + Math.cosh(2 * a);
|
|
|
|
return new Complex(
|
|
2 * Math.cosh(a) * Math.cos(b) / d,
|
|
-2 * Math.sinh(a) * Math.sin(b) / d);
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex asinh
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"asinh": function() {
|
|
|
|
var tmp = this["im"];
|
|
this["im"] = -this["re"];
|
|
this["re"] = tmp;
|
|
var res = this["asin"]();
|
|
|
|
this["re"] = -this["im"];
|
|
this["im"] = tmp;
|
|
tmp = res["re"];
|
|
|
|
res["re"] = -res["im"];
|
|
res["im"] = tmp;
|
|
return res;
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex asinh
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"acosh": function() {
|
|
|
|
var tmp;
|
|
var res = this["acos"]();
|
|
if (res["im"] <= 0) {
|
|
tmp = res["re"];
|
|
res["re"] = -res["im"];
|
|
res["im"] = tmp;
|
|
} else {
|
|
tmp = res["im"];
|
|
res["im"] = -res["re"];
|
|
res["re"] = tmp;
|
|
}
|
|
return res;
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex atanh
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"atanh": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
var noIM = a > 1 && b === 0;
|
|
var oneMinus = 1 - a;
|
|
var onePlus = 1 + a;
|
|
var d = oneMinus * oneMinus + b * b;
|
|
|
|
var x = (d !== 0)
|
|
? new Complex(
|
|
(onePlus * oneMinus - b * b) / d,
|
|
(b * oneMinus + onePlus * b) / d)
|
|
: new Complex(
|
|
(a !== -1) ? (a / 0) : 0,
|
|
(b !== 0) ? (b / 0) : 0);
|
|
|
|
var temp = x["re"];
|
|
x["re"] = logHypot(x["re"], x["im"]) / 2;
|
|
x["im"] = Math.atan2(x["im"], temp) / 2;
|
|
if (noIM) {
|
|
x["im"] = -x["im"];
|
|
}
|
|
return x;
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex acoth
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"acoth": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
if (a === 0 && b === 0) {
|
|
|
|
return new Complex(0, Math.PI / 2);
|
|
}
|
|
|
|
var d = a * a + b * b;
|
|
return (d !== 0)
|
|
? new Complex(
|
|
a / d,
|
|
-b / d).atanh()
|
|
: new Complex(
|
|
(a !== 0) ? a / 0 : 0,
|
|
(b !== 0) ?-b / 0 : 0).atanh();
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex acsch
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"acsch": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
if (b === 0) {
|
|
|
|
return new Complex(
|
|
(a !== 0)
|
|
? Math.log(a + Math.sqrt(a * a + 1))
|
|
: Infinity, 0);
|
|
}
|
|
|
|
var d = a * a + b * b;
|
|
return (d !== 0)
|
|
? new Complex(
|
|
a / d,
|
|
-b / d).asinh()
|
|
: new Complex(
|
|
(a !== 0) ? a / 0 : 0,
|
|
(b !== 0) ?-b / 0 : 0).asinh();
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex asech
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"asech": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
if (a === 0 && b === 0) {
|
|
return new Complex(Infinity, 0);
|
|
}
|
|
|
|
var d = a * a + b * b;
|
|
return (d !== 0)
|
|
? new Complex(
|
|
a / d,
|
|
-b / d).acosh()
|
|
: new Complex(
|
|
(a !== 0) ? a / 0 : 0,
|
|
(b !== 0) ?-b / 0 : 0).acosh();
|
|
},
|
|
|
|
/**
|
|
* Calculate the complex inverse 1/z
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"inverse": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
|
|
var d = a * a + b * b;
|
|
|
|
return new Complex(
|
|
a !== 0 ? a / d : 0,
|
|
b !== 0 ?-b / d : 0);
|
|
},
|
|
|
|
/**
|
|
* Returns the complex conjugate
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"conjugate": function() {
|
|
|
|
return new Complex(this["re"], -this["im"]);
|
|
},
|
|
|
|
/**
|
|
* Gets the negated complex number
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"neg": function() {
|
|
|
|
return new Complex(-this["re"], -this["im"]);
|
|
},
|
|
|
|
/**
|
|
* Ceils the actual complex number
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"ceil": function(places) {
|
|
|
|
places = Math.pow(10, places || 0);
|
|
|
|
return new Complex(
|
|
Math.ceil(this["re"] * places) / places,
|
|
Math.ceil(this["im"] * places) / places);
|
|
},
|
|
|
|
/**
|
|
* Floors the actual complex number
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"floor": function(places) {
|
|
|
|
places = Math.pow(10, places || 0);
|
|
|
|
return new Complex(
|
|
Math.floor(this["re"] * places) / places,
|
|
Math.floor(this["im"] * places) / places);
|
|
},
|
|
|
|
/**
|
|
* Ceils the actual complex number
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"round": function(places) {
|
|
|
|
places = Math.pow(10, places || 0);
|
|
|
|
return new Complex(
|
|
Math.round(this["re"] * places) / places,
|
|
Math.round(this["im"] * places) / places);
|
|
},
|
|
|
|
/**
|
|
* Compares two complex numbers
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
"equals": function(a, b) {
|
|
|
|
parse(a, b); // mutates P
|
|
|
|
return Math.abs(P["re"] - this["re"]) <= Complex["EPSILON"] &&
|
|
Math.abs(P["im"] - this["im"]) <= Complex["EPSILON"];
|
|
},
|
|
|
|
/**
|
|
* Clones the actual object
|
|
*
|
|
* @returns {Complex}
|
|
*/
|
|
"clone": function() {
|
|
|
|
return new Complex(this["re"], this["im"]);
|
|
},
|
|
|
|
/**
|
|
* Gets a string of the actual complex number
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
"toString": function() {
|
|
|
|
var a = this["re"];
|
|
var b = this["im"];
|
|
var ret = "";
|
|
|
|
if (isNaN(a) || isNaN(b)) {
|
|
return "NaN";
|
|
}
|
|
|
|
if (a !== 0) {
|
|
ret+= a;
|
|
}
|
|
|
|
if (b !== 0) {
|
|
|
|
if (a !== 0) {
|
|
ret+= b < 0 ? " - " : " + ";
|
|
} else if (b < 0) {
|
|
ret+= "-";
|
|
}
|
|
|
|
b = Math.abs(b);
|
|
|
|
if (1 !== b) {
|
|
ret+= b;
|
|
}
|
|
ret+= "i";
|
|
}
|
|
|
|
if (!ret)
|
|
return "0";
|
|
|
|
return ret;
|
|
},
|
|
|
|
/**
|
|
* Returns the actual number as a vector
|
|
*
|
|
* @returns {Array}
|
|
*/
|
|
"toVector": function() {
|
|
|
|
return [this["re"], this["im"]];
|
|
},
|
|
|
|
/**
|
|
* Returns the actual real value of the current object
|
|
*
|
|
* @returns {number|null}
|
|
*/
|
|
"valueOf": function() {
|
|
|
|
if (this["im"] === 0) {
|
|
return this["re"];
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* Checks if the given complex number is not a number
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
isNaN: function() {
|
|
return isNaN(this['re']) || isNaN(this['im']);
|
|
}
|
|
};
|
|
|
|
Complex["ZERO"] = new Complex(0, 0);
|
|
Complex["ONE"] = new Complex(1, 0);
|
|
Complex["I"] = new Complex(0, 1);
|
|
Complex["PI"] = new Complex(Math.PI, 0);
|
|
Complex["E"] = new Complex(Math.E, 0);
|
|
Complex['EPSILON'] = 1e-16;
|
|
|
|
if (typeof define === "function" && define["amd"]) {
|
|
define([], function() {
|
|
return Complex;
|
|
});
|
|
} else if (typeof exports === "object") {
|
|
module["exports"] = Complex;
|
|
} else {
|
|
root["Complex"] = Complex;
|
|
}
|
|
|
|
})(this);
|