dockerized_openAger/nodered/rootfs/data/node_modules/complex.js/complex.js
2020-10-17 18:42:50 +02:00

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);