From dcac90216ff5f017f4952e0dc6faaeab07ebcd8d Mon Sep 17 00:00:00 2001 From: Patrick Roberts Date: Mon, 25 Aug 2014 14:15:08 -0500 Subject: [PATCH] Updated comments and toString method --- lib/complex.js | 872 +++++++++++++++++++++++++++++++-------------- lib/complex.min.js | 2 +- package.json | 2 +- 3 files changed, 599 insertions(+), 277 deletions(-) diff --git a/lib/complex.js b/lib/complex.js index 769a87c..2d46fa7 100644 --- a/lib/complex.js +++ b/lib/complex.js @@ -1,83 +1,88 @@ -/* -// Copyright (c) 2012 Patrick Roberts -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without restriction, -// including without limitation the rights to use, copy, modify, -// merge, publish, distribute, sublicense, and/or sell copies of the -// Software, and to permit persons to whom the Software is furnished -// to do so, subject to the following conditions: -// -// included in all copies or substantial portions of the Software. -// -// The above copyright notice and this permission notice shall be -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -*/ -(function () { +/** + * Copyright (c) 2012 Patrick Roberts + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * included in all copies or substantial portions of the Software. + * + * The above copyright notice and this permission notice shall be + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +(function() { // localize global scope and Math var root = this, Math = root.Math; // Math object helper functions for more advanced formulas - Math.sinh = function (x) { + Math.sinh = function(x) { return(-Math.exp(-x) + Math.exp(x)) / 2; }; - Math.cosh = function (x) { + Math.cosh = function(x) { return(Math.exp(-x) + Math.exp(x)) / 2; }; - var floor = function (x) { - return(x >= 0 ? x - x % 1 : x - (x % 1 + 1)); - }, - round = function (x) { - x += .5; - return(x >= 0 ? x - x % 1 : x - (x % 1 + 1)); - }, - ceil = function (x) { - return(x >= 0 ? x - (x % 1 + 1) : x - x % 1); - }, - PI = Math.PI, - g = 7, + function floor(x) { + return(x >= 0 ? x - x % 1 : x - (x % 1 + 1)); + } + + function round(x) { + x += .5; + return(x >= 0 ? x - x % 1 : x - (x % 1 + 1)); + } + + function ceil(x) { + return(x >= 0 ? x - (x % 1 + 1) : x - x % 1); + } + + // local variables for gamma function + var g = 7, index = [], p = [ 0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7 ] - .map(function (coef, i) { + .map(function(coef, i) { index.push(Complex(i, 0, i, 0)); return Complex(coef, 0, Math.abs(coef), coef < 0 ? PI : 0); - }), + }); + + // localize these commonly used constants + var PI = Math.PI, SQRT2PI = Complex(Math.sqrt(2 * PI), 0, Math.sqrt(2 * PI), 0); - // Component constructor for Complex class - // r:Number (required) - // the Real part - // i:Number (required) - // the Imaginary part - // m:Number (optional) - // the Magnitude - // t:Number (optional) - // the Argument - // Note: when calling this constructor, it - // is recommended to omit the second - // two arguments, unless they have - // been accurately calculated. + /** + * Complex constructor + * optionally instantiate with `new` + * + * @param {Number} real part + * @param {Number} imaginary part + * @param {Number} optional, magnitude + * @param {Number} optional, argument + * @return {Object} complex number + */ + function Complex(r, i, m, t) { var invoked = this instanceof Complex, self = invoked ? this : new Complex(r, i, m, t); if(!invoked) return self; - self.m = (typeof m == "number" ? Math.abs(m) : Math.sqrt(r * r + i * i)); - if(typeof t == "number") { + self.m = (typeof m === "number" ? Math.abs(m) : Math.sqrt(r * r + i * i)); + if(typeof t === "number") { self.t = (m >= 0 ? t : t + PI); } else { self.t = Math.atan2(i, r); @@ -88,124 +93,225 @@ self.i = i; } - // Use for displaying Complex numbers as a string - // type:Boolean - // if true, the display is a+bi form - // if false or omitted, the display is r e^(ti) form - Complex.prototype.toString = function (type) { + /** + * toString method + * outputs complex number as string + * + * @param {Boolean} optional, output as cartesian, default is exponential + * @return {String} + */ + + Complex.prototype.toString = function(type) { if(!Complex.isFinite(this)) return "Infinity"; if(Complex.isNaN(this)) return "NaN"; if(type) { - var r = this.r.toPrecision().toUpperCase(), - i = this.i.toPrecision().toUpperCase(); + var r = this.r.toPrecision() + .toUpperCase(), + i = this.i.toPrecision() + .toUpperCase(); if(this.i === 0) return r; - if(this.r === 0) return (this.i === 1 ? "" : i + " ") + "i"; - return r + (this.i === 1 ? "" : (this.i > 0 ? "+" + i + " " : i + " ")) + "i"; + if(this.r === 0) return Math.abs(this.i) === 1 ? (this.i > 0 ? "i" : "-i") : i + " i"; + return r + (this.i > 0 ? "+" : "") + (Math.abs(this.i) === 1 ? (this.i > 0 ? "i" : "-i") : i + " i"); } else { - var m = this.m.toPrecision().toUpperCase(), - t = this.t.toPrecision().toUpperCase(); + var m = this.m.toPrecision() + .toUpperCase(), + t = this.t.toPrecision() + .toUpperCase(); if(this.m === 0 || this.t === 0) return m; return m + " e^(" + t + " i)"; } }; - // Exponent function - Complex.exp = function (c) { + /** + * Exponential function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.exp = function(c) { // formula: e^(a+bi) = e^a e^(bi) - return c.i == 0 ? Complex["E"].pow(c.r) : Complex.Polar(Math.exp(c.r), c.i); + return c.i === 0 ? Complex["E"].pow(c.r) : Complex.Polar(Math.exp(c.r), c.i); }; - // Polar constructor for Complex class - // r:Number (required) - // the Magnitude - // t:Number (required) - // the Argument - Complex.Polar = function (r, t) { + /** + * Polar Complex constructor + * Do NOT instantiate with `new` + * + * @param {Number} magnitude + * @param {Number} argument + * @return {Object} complex number + */ + + Complex.Polar = function(r, t) { return new Complex(r * Math.cos(t), r * Math.sin(t), r, t); }; - // Addition operator - Complex.prototype.add = function (c) { + /** + * Addition operator + * `this` is augend + * + * @param {Object} complex addend + * @return {Object} complex sum + */ + + Complex.prototype.add = function(c) { return Complex(this.r + c.r, this.i + c.i); }; - // Subtraction operator - Complex.prototype.sub = function (c) { + /** + * Subtraction operator + * `this` is minuend + * + * @param {Object} complex subtrahend + * @return {Object} complex difference + */ + + Complex.prototype.sub = function(c) { return Complex(this.r - c.r, this.i - c.i); }; - // Floor function - // integerizes the two components separately - Complex.floor = function (c) { + /** + * Floor function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.floor = function(c) { return Complex(floor(c.r), floor(c.i)); }; - // Ceiling function - // Rounds up the two compenents separately - Complex.ceil = function (c) { + /** + * Ceiling function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.ceil = function(c) { return Complex(ceil(c.r), ceil(c.i)); }; - // Conjugate function - // Reverses the sign of the imaginary component - Complex.conj = function (c) { + /** + * Complex Conjugate function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.conj = function(c) { return Complex(c.r, -c.i, c.m, (PI * 2 - c.t) % PI * 2); }; - // Multiplication operator - Complex.prototype.mult = function (c) { + /** + * Multiplication operator + * `this` is multiplicand + * + * @param {Object} complex multiplier + * @return {Object} complex product + */ + + Complex.prototype.mult = function(c) { // formula: r1 e^(t1i) * r2 e^(t2i) = r1r2 e^((t1+t2)i) return Complex.Polar(this.m * c.m, this.t + c.t); }; - // Division operator - Complex.prototype.divBy = function (c) { + /** + * Division operator + * `this` is dividend + * + * @param {Object} complex divisor + * @return {Object} complex quotient + */ + + Complex.prototype.divBy = function(c) { // formula: r1 e^(t1i) / (r2 e^(t2i)) = r1/r2 e^((t1-t2)i) return Complex.Polar(this.m / c.m, this.t - c.t); }; - // Square function - Complex.square = function (c) { + /** + * Square function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.square = function(c) { // formula: (a+bi)^2 = (a^2-b^2) + (2ab)i return Complex(c.r * c.r - c.i * c.i, 2 * c.r * c.i, c.m * c.m, c.t * 2); }; - // Cube function - Complex.cube = function (c) { + /** + * Cube function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.cube = function(c) { // formula: (a+bi)^3 = (a^3-3ab^2) + (3a^2b-b^3)i return Complex(c.r * c.r * c.r - 3 * c.r * c.i * c.i, 3 * c.r * c.r * c.i - c.i * c.i * c.i, c.m * c.m * c.m, c.t * 3); }; - // Real Power operator - // z:Number - // MUST be a Number, not a Complex - Complex.prototype.pow = function (z) { - if(z == 2) return Complex.square(this); - if(z == 3) return Complex.cube(this); + /** + * Real Power operator + * `this` is base + * + * @param {Number} real exponent + * @return {Object} complex power + */ + + Complex.prototype.pow = function(z) { + if(z === 2) return Complex.square(this); + if(z === 3) return Complex.cube(this); // formula: (r e^(ti))^(z) = r^z e^(tzi) return Complex.Polar(Math.pow(this.m, z), this.t * z); }; - // Square Root function - Complex.sqrt = function (c) { + /** + * Square Root function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.sqrt = function(c) { return c.pow(.5); }; - // Cube Root function - Complex.cbrt = function (c) { + /** + * Cube Root function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.cbrt = function(c) { return c.pow(1 / 3); } - // Complex Power operator - // c:Complex - // MUST be a Complex, not a Number - Complex.prototype.cPow = function (c) { + /** + * Complex Power operator + * `this` is base + * + * @param {Complex} complex exponent + * @return {Object} complex power + */ + + Complex.prototype.cPow = function(c) { // formula: (r e^(ti))^(a+bi) = (r^a e^(-bt)) e^((ln(r)b+at)i) - return c.i == 0 ? this.pow(c.r) : Complex.Polar(Math.pow(this.m, c.r) * Math.exp(-c.i * this.t), c.i * Math.log(this.m) + c.r * this.t); + return c.i === 0 ? this.pow(c.r) : Complex.Polar(Math.pow(this.m, c.r) * Math.exp(-c.i * this.t), c.i * Math.log(this.m) + c.r * this.t); }; - // Gamma function - Complex.gamma = function (c) { + /** + * Gamma function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.gamma = function(c) { if(c.r < .5) return Complex["PI"].divBy(Complex.sin(Complex["PI"].mult(c)) .mult(Complex.gamma(Complex["1"].sub(c)))); else { @@ -220,13 +326,25 @@ } }; - // Factorial function - Complex.fact = function (c) { + /** + * Factorial function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.fact = function(c) { return Complex.gamma(c.add(Complex["1"])); } - // Cosine function - Complex.cos = function (c) { + /** + * Cosine function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.cos = function(c) { // formula: cos(c) = (e^(ci)+e^(-ci))/2 var ci = c.mult(Complex["I"]); return Complex.exp(ci) @@ -234,8 +352,14 @@ .divBy(Complex["2"]); }; - // Sine function - Complex.sin = function (c) { + /** + * Sine function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.sin = function(c) { // formula: sin(c) = (e^(ci)-e^(-ci))/(2i) var ci = c.mult(Complex["I"]); return Complex.exp(ci) @@ -243,8 +367,14 @@ .divBy(Complex["2I"]); }; - // Tangent function - Complex.tan = function (c) { + /** + * Tangent function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.tan = function(c) { // formula: tan(c) = (e^(ci)-e^(-ci))/(i(e^(ci)+e^(-ci))) var ci = c.mult(Complex["I"]), pex = Complex.exp(ci), @@ -254,24 +384,42 @@ .mult(Complex["I"])); }; - // Secant function - Complex.sec = function (c) { + /** + * Secant function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.sec = function(c) { // formula: sec(c) = 2/(e^(ci)+e^(-ci)) var ci = c.mult(Complex["I"]); return Complex["2"].divBy(Complex.exp(ci) .add(Complex.exp(Complex.neg(ci)))); }; - // Cosecant function - Complex.csc = function (c) { + /** + * Cosecant function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.csc = function(c) { // formula: csc(c) = 2i/(e^(ci)-e^(-ci)) var ci = c.mult(Complex["I"]); return Complex["2I"].divBy(Complex.exp(ci) .sub(Complex.exp(Complex.neg(ci)))); }; - // Cotangent function - Complex.cot = function (c) { + /** + * Cotangent function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.cot = function(c) { // formula: cot(c) = i(e^(ci)+e^(-ci))/(e^(ci)-e^(-ci)) var ci = c.mult(Complex["I"]), pex = Complex.exp(ci), @@ -281,27 +429,51 @@ .divBy(pex.sub(nex)); }; - // Natural Logarithm function - Complex.log = function (c) { + /** + * Natural Logarithm function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.log = function(c) { // formula: log(r e^(ti)) = log(r)+ti return Complex(Math.log(c.m), c.t); }; - // Inverse Sine function - Complex.arcsin = function (c) { + /** + * Inverse Sine function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arcsin = function(c) { // formula: arcsin(c) = -i*log(ic+sqrt(1-c^2)) return Complex["-I"].mult(Complex.log(Complex["I"].mult(c) .add(Complex.sqrt(Complex["1"].sub(c.pow(2)))))); }; - // Inverse Cosine function - Complex.arccos = function (c) { + /** + * Inverse Cosine function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arccos = function(c) { // formula: arccos(c) = i*log(c-i*sqrt(1-c^2)) return Complex["I"].mult(Complex.log(c.add(Complex["-I"].mult(Complex.sqrt(Complex["1"].sub(c.pow(2))))))); }; - // Inverse Tangent function - Complex.arctan = function (c) { + /** + * Inverse Tangent function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arctan = function(c) { // formula: arctan(c) = i/2 log((i+x)/(i-x)) var i = Complex["I"]; return i.mult(Complex.log(i.add(c) @@ -309,22 +481,40 @@ .divBy(Complex["2"]); }; - // Inverse Secant function - Complex.arcsec = function (c) { + /** + * Inverse Secant function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arcsec = function(c) { // formula: arcsec(c) = -i*log(1/c+sqrt(1-i/c^2)) return Complex["-I"].mult(Complex.log(c.pow(-1) .add(Complex.sqrt(Complex["1"].sub(Complex["I"].divBy(c.pow(2))))))); }; - // Inverse Cosecant function - Complex.arccsc = function (c) { + /** + * Inverse Cosecant function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arccsc = function(c) { // formula: arccsc(c) = -i*log(i/c+sqrt(1-1/c^2)) return Complex["-I"].mult(Complex.log(Complex["I"].divBy(c) .add(Complex.sqrt(Complex["1"].sub(c.pow(-2)))))); }; - // Inverse Cotangent function - Complex.arccot = function (c) { + /** + * Inverse Cotangent function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arccot = function(c) { // formula: arccot(c) = i/2 log((c-i)/(c+i)) var i = Complex["I"]; return i.mult(Complex.log(c.sub(i) @@ -332,24 +522,42 @@ .divBy(Complex["2"]); }; - // Hyperbolic Sine function - Complex.sinh = function (c) { + /** + * Hyperbolic Sine function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.sinh = function(c) { // formula: sinh(c) = (e^c-e^-c)/2 return Complex.exp(c) .sub(Complex.exp(Complex.neg(c))) .divBy(Complex["2"]); }; - // Hyperbolic Cosine function - Complex.cosh = function (c) { + /** + * Hyperbolic Cosine function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.cosh = function(c) { // formula: cosh(c) = (e^c+e^-c)/2 return Complex.exp(c) .add(Complex.exp(Complex.neg(c))) .divBy(Complex["2"]); }; - // Hyperbolic Tangent function - Complex.tanh = function (c) { + /** + * Hyperbolic Tangent function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.tanh = function(c) { // formula: tanh(c) = (e^c-e^-c)/(e^c+e^-c) var pex = Complex.exp(c), nex = Complex.exp(Complex.neg(c)); @@ -357,22 +565,40 @@ .divBy(pex.add(nex)); }; - // Hyperbolic Cosecant function - Complex.csch = function (c) { - // formula: csch(c) = 2/(e^c-e^-c) - return Complex["2"].divBy(Complex.exp(c) - .sub(Complex.exp(Complex.neg(c)))); - }; + /** + * Hyperbolic Secant function + * + * @param {Object} complex number + * @return {Object} complex number + */ - // Hyperbolic Secant function - Complex.sech = function (c) { + Complex.sech = function(c) { // formula: sech(c) = 2/(e^c+e^-c) return Complex["2"].divBy(Complex.exp(c) .add(Complex.exp(Complex.neg(c)))); }; - // Hyperbolic Cotangent function - Complex.coth = function (c) { + /** + * Hyperbolic Cosecant function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.csch = function(c) { + // formula: csch(c) = 2/(e^c-e^-c) + return Complex["2"].divBy(Complex.exp(c) + .sub(Complex.exp(Complex.neg(c)))); + }; + + /** + * Hyperbolic Cotangent function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.coth = function(c) { // formula: coth(c) = (e^c+e^-c)/(e^c-e^-c) var pex = Complex.exp(c), nex = Complex.exp(Complex.neg(c)); @@ -380,150 +606,246 @@ .divBy(pex.sub(nex)); }; - // Inverse Hyperbolic Sine function - Complex.arcsinh = function (c) { + /** + * Inverse Hyperbolic Sine function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arcsinh = function(c) { // formula: arcsinh(c) = log(c+sqrt(c^2+1)) return Complex.log(c.add(Complex.sqrt(c.pow(2) .add(Complex["1"])))); }; - // Inverse Hyperbolic Cosine function - Complex.arccosh = function (c) { + /** + * Inverse Hyperbolic Cosine function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arccosh = function(c) { // formula: arccosh(c) = log(c+sqrt(c^2-1)) return Complex.log(c.add(Complex.sqrt(c.pow(2) .sub(Complex["1"])))); }; - // Inverse Hyperbolic Tangent function - Complex.arctanh = function (c) { + /** + * Inverse Hyperbolic Tangent function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arctanh = function(c) { // formula: arctanh(c) = 1/2 log((1+c)/(1-c)) return Complex.log(Complex["1"].add(c) .divBy(Complex["1"].sub(c))) .divBy(Complex["2"]); }; - // Inverse Hyperbolic Secant function - Complex.arcsech = function (c) { + /** + * Inverse Hyperbolic Secant function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arcsech = function(c) { // formula: arcsech(c) = log((1+sqrt(1-c^2))/c) return Complex.log(Complex["1"].add(Complex.sqrt(Complex["1"].sub(c.pow(2)))) .divBy(c)); }; - // Inverse Hyperbolic Cosecant function - Complex.arccsch = function (c) { + /** + * Inverse Hyperbolic Cosecant function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arccsch = function(c) { // formula: arccsch(c) = log((1+sqrt(1+c^2))/c) return Complex.log(Complex["1"].add(Complex.sqrt(Complex["1"].add(c.pow(2)))) .divBy(c)); }; - // Inverse Hyperbolic Cotangent function - Complex.arccoth = function (c) { + /** + * Inverse Hyperbolic Cotangent function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arccoth = function(c) { // formula: arccoth(c) = 1/2 log((c+1)/(c-1)) return Complex.log(c.add(Complex["1"]) .divBy(c.sub(Complex["1"]))) .divBy(Complex["2"]); }; - // Negative function - Complex.neg = function (c) { + /** + * Negative function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.neg = function(c) { return Complex(-c.r, -c.i, c.m, c.t + PI); }; - // Real function - // Returns the real value as a Complex - Complex.re = function (c) { + /** + * Real Part function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.re = function(c) { return Complex(c.r, 0, Math.abs(c.r), c.r < 0 ? PI : 0); }; - // Imaginary function - // Returns the imaginary value as a Complex - // Note that this function stores the - // imaginary value in the real component - Complex.im = function (c) { + /** + * Imaginary Part function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.im = function(c) { return Complex(c.i, 0, Math.abs(c.i), c.i < 0 ? PI : 0); }; - // Absolute value function - // Returns the absolute value as a Complex - Complex.abs = function (c) { + /** + * Absolute Value function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.abs = function(c) { return Complex(c.m, 0, c.m, 0); }; - // Argument function - // Returns the argument as a Complex - Complex.arg = function (c) { + /** + * Argument function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.arg = function(c) { return Complex(c.t, 0, c.t, 0); }; - // Round function - // Rounds the components separately - Complex.round = function (c) { + /** + * Integer Rounding function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.round = function(c) { return Complex(round(c.r), round(c.i)); }; - // Fractional Part function - // Returns the fractional part of each component separately - Complex.fPart = function (c) { + /** + * Fractional Part function + * + * @param {Object} complex number + * @return {Object} complex number + */ + + Complex.fPart = function(c) { return Complex(c.r - floor(c.r), c.i - floor(c.i)); }; - // Modulus operator - // Returns the modulus of each component separately - Complex.prototype.mod = function (c) { + /** + * Modulation operator + * `this` is dividend + * + * @param {Object} complex divisor + * @return {Object} complex remainder + */ + + Complex.prototype.mod = function(c) { return Complex(c.r === 0 ? 0 : this.r % c.r, c.i === 0 ? 0 : this.i % c.i); }; - // Mininum function - // Returns the Complex with the smallest magnitude - Complex.min = function () { - var args = Array.prototype.slice.call( + /** + * Minimum function (by absolute value) + * + * @params {Object} complex numbers + * @return {Object} complex number + */ + + Complex.min = function() { + var args = [].slice.call( arguments ) - .map(function (c) { + .map(function(c) { return c.m; }), min = Math.min.apply(Math, args); return arguments[args.indexOf(min)]; }; - // Maximum function - // Returns the Complex with the largest magnitude - Complex.max = function () { - var args = Array.prototype.slice.call( + /** + * Maximum function (by absolute value) + * + * @params {Object} complex numbers + * @return {Object} complex number + */ + + Complex.max = function() { + var args = [].slice.call( arguments ) - .map(function (c) { + .map(function(c) { return c.m; }), max = Math.max.apply(Math, args); return arguments[args.indexOf(max)]; } - // Determines whether the Complex is validly formed or not - // Returns a Boolean - Complex.isNaN = function (c) { + /** + * isNaN method + * determines if complex number has any malformed components + * + * @param {Object} complex number + * @return {Boolean} isNaN + */ + + Complex.isNaN = function(c) { return isNaN(c.r) || isNaN(c.i) || isNaN(c.m) || isNaN(c.t); }; - // Determines whether the Complex is finite or not - // Returns a Boolean - Complex.isFinite = function (c) { + /** + * isFinite function + * determines if complex number has a finite magnitude + * + * @param {Object} complex number + * @return {Boolean} isFinite + */ + + Complex.isFinite = function(c) { return isFinite(c.m); }; - // Complex expression parser - // str:String (required) - // human-readable Complex math expression - // args:Array[String] (optional) - // array of arguments for the expected function, - // each index containing the name of the variable - // in the human-readable string - // skipFormat:Boolean (optional) - // if false or omitted, auto-formats math expression - // it is recommended to call Complex.formatFunction - // on string passed before calling Complex.parseFunction - // with skipFormat set to true - Complex.parseFunction = function (str, args, skipFormat) { + /** + * parseFunction method + * compiles a human-readable math expression into callable function + * + * @param {String} human-readable math expression + * @param {Array} optional, ordered array of human-readable variables + * @param {Boolean} optional, skip pre-formatting, defaults to false + * @return {Function} complex math function + */ + + Complex.parseFunction = function(str, args, skipFormat) { with(this) { // set the scope to the context (see below for included variables) // generate map of compiled variable names var vars = generate(args = args || []); @@ -535,7 +857,7 @@ // the constructed function being returned return new Function( // arguments being mapped to compiled names - args.map(function (arg) { + args.map(function(arg) { return vars[arg]; }) .join(","), @@ -548,7 +870,7 @@ while(i < exp.length) { // although e is a constant, the exponent function is much more efficient than the complex power function if(lastObj != 2 && exp.substr(i, 2) != "e^" && exp.substr(i) - .search(varsExp) == 0) { // attempts to parse a variable + .search(varsExp) === 0) { // attempts to parse a variable var match = exp.substr(i) .match(varsExp)[0], name = varsObj[match] || vars[match]; @@ -559,13 +881,13 @@ continue; } if(lastObj != 2 && exp.substr(i) - .search(funcExp) == 0) { // attempts to parse a function + .search(funcExp) === 0) { // attempts to parse a function var match = exp.substr(i) .match(funcExp)[0], func = funcObj[match], // determines functional group to which to apply function // the exceptions - and e^ do not simply look for ) to end functional group - j = match == "-" ? nextAddSub(exp, i + match.length) : match == "e^" ? nextPower(exp, i + match.length) : endNest(exp, i + match.length); + j = match === "-" ? nextAddSub(exp, i + match.length) : match === "e^" ? nextPower(exp, i + match.length) : endNest(exp, i + match.length); if(!func) { // instead of throwing exception, attempt to resolve by assuming implied multiplication first match = match.substr(0, match.length - 1); // strips off "("; // if this fails it's hopeless @@ -574,33 +896,33 @@ exp = exp.substr(0, i) + match + "*(" + exp.substr(i + match.length + 1); continue; } - str += func == "(" ? "" : func; // ignores extraneous grouping + str += func === "(" ? "" : func; // ignores extraneous grouping i += match.length; - str += self(exp.substring(i, j)) + (func == "(" ? "" : ")"); - i = exp[j] == ")" ? j + 1 : j; + str += self(exp.substring(i, j)) + (func === "(" ? "" : ")"); + i = exp[j] === ")" ? j + 1 : j; lastObj = 2; continue; } - if(lastObj == 2 && exp.substr(i) - .search(operExp) == 0) { // attempts to parse an operator + if(lastObj === 2 && exp.substr(i) + .search(operExp) === 0) { // attempts to parse an operator var match = exp.substr(i) .match(operExp)[0], oper = operObj[match], // ensures that nextOperator skips the implicit operator of a float, e.g. the minus in "2e-1" num = exp.substr(i + 1) - .search(numsExp) == 0 && exp.substr(i + 1) + .search(numsExp) === 0 && exp.substr(i + 1) .match(numsExp)[0], j = nextOperator(match, exp, i + match.length + (num ? num.length : 0)); if(!oper) throw '"' + match + '" is not a valid operator.'; str += oper; i += match.length; str += self(exp.substring(i, j)) + ")"; - i = exp[j] == ")" ? j + 1 : j; + i = exp[j] === ")" ? j + 1 : j; lastObj = 2; continue; } if(lastObj != 2 && exp.substr(i) - .search(numsExp) == 0) { // attempts to parse a number + .search(numsExp) === 0) { // attempts to parse a number var match = exp.substr(i) .match(numsExp)[0], nums = parseFloat(match); @@ -622,7 +944,7 @@ }; // Create scope of Complex.parseFunction - Complex.parseFunction = Complex.parseFunction.bind(function () { + Complex.parseFunction = Complex.parseFunction.bind(function() { var formExp = /(?:\d[a-z\{\[\(]|[\}\]\)][a-z\d])|(?:[\{\}\[\]]| *[\+\-\*\/\^ %] *)/gi, // matches commonly used unsupported formatting in human-readable expressions funcExp = /[a-z]*\(|(?:-|e\^)(?:\(|)/, // matches either as few as zero continuous a-z followed by ( OR -,e^ optionally followed by ( varsExp = /[a-z]+(?![a-z]*\()/, // matches continuous a-z NOT followed by ( @@ -710,7 +1032,7 @@ "i": "this.Complex.I", "pi": "this.Complex.PI" }, - formatFunction = function (str) { // optionally called before compilation in order to repair unsupported formatting + formatFunction = function(str) { // optionally called before compilation in order to repair unsupported formatting var open = /\{|\[/, close = /\}|\]/, // catches implied multiplication like "3i" or ")x" @@ -737,10 +1059,10 @@ } }); }, - generate = function (args) { // maps the arguments to arbitrarily generated names + generate = function(args) { // maps the arguments to arbitrarily generated names var rmChars = /[^a-z]+/i; // characters to be removed from variable names supplied // fix and validate them first - args = args.map(function (arg) { + args = args.map(function(arg) { var name = arg.replace(rmChars, ""); if(!name) throw '"' + arg + '" is an invalid variable name. Only "a" through "z" are accepted within names. "e", "i" and "pi", will be calculated as the constants.'; return name.toLowerCase(); @@ -764,71 +1086,71 @@ } return map; }, - endNest = function (str, i) { // returns last index of grouped expression located at i + endNest = function(str, i) { // returns last index of grouped expression located at i var nest = 1; - while(str[i] == "(" ? nest++ : (str[i] == ")" ? --nest : i < str.length)) i++; + while(str[i] === "(" ? nest++ : (str[i] === ")" ? --nest : i < str.length)) i++; return i; }, - nextAddSub = function (str, i) { // returns last index of expression to add or subtract located at i based on order of operations + nextAddSub = function(str, i) { // returns last index of expression to add or subtract located at i based on order of operations var nest = 0; for(var c = i; c < str.length; c++) { - if(str[c] == "(") nest++; - else if(str[c] == ")") nest--; + if(str[c] === "(") nest++; + else if(str[c] === ")") nest--; if(nest < 0) return c; - else if(nest == 0) { - if(str[c] == "+" || (str[c] == "-" && !operObj[str[c - 1]])) return c; + else if(nest === 0) { + if(str[c] === "+" || (str[c] === "-" && !operObj[str[c - 1]])) return c; } } return str.length; }, - nextMultDiv = function (str, i) { // returns last index of expression to multiply or divide located at i based on order of operations + nextMultDiv = function(str, i) { // returns last index of expression to multiply or divide located at i based on order of operations var nest = 0; for(var c = i; c < str.length; c++) { - if(str[c] == "(") nest++; - else if(str[c] == ")") nest--; + if(str[c] === "(") nest++; + else if(str[c] === ")") nest--; if(nest < 0) return c; - else if(nest == 0) { - if(str[c] == "+" || (str[c] == "-" && !operObj[str[c - 1]])) return c; - if(str[c] == "*" || str[c] == "/" || str[c] == " " || str[c] == "%") return c; + else if(nest === 0) { + if(str[c] === "+" || (str[c] === "-" && !operObj[str[c - 1]])) return c; + if(str[c] === "*" || str[c] === "/" || str[c] === " " || str[c] === "%") return c; } } return str.length; }, - nextPower = function (str, i) { // returns last index of expression to raise to located at i based on order of operations + nextPower = function(str, i) { // returns last index of expression to raise to located at i based on order of operations var nest = 0; for(var c = i; c < str.length; c++) { - if(str[c] == "(") nest++; - else if(str[c] == ")") nest--; + if(str[c] === "(") nest++; + else if(str[c] === ")") nest--; if(nest < 0) return c; - else if(nest == 0) { - if(str[c] == "+" || (str[c] == "-" && str[c - 1] != "^")) return c; - if(str[c] == "*" || str[c] == "/" || str[c] == " " || str[c] == "%") return c; - if(str[c] == "^") return c; + else if(nest === 0) { + if(str[c] === "+" || (str[c] === "-" && str[c - 1] != "^")) return c; + if(str[c] === "*" || str[c] === "/" || str[c] === " " || str[c] === "%") return c; + if(str[c] === "^") return c; } } return str.length; }, - nextOperator = function (oper, str, i) { // switches operator to determine which order of operations to apply + nextOperator = function(oper, str, i) { // switches operator to determine which order of operations to apply switch(oper) { - case "+": - case "-": - return nextAddSub(str, i); - case "*": - case "/": - case " ": - case "%": - return nextMultDiv(str, i); - case "^": - return nextPower(str, i); - default: - throw '"' + oper + '" is not a valid operator.'; + case "+": + case "-": + return nextAddSub(str, i); + case "*": + case "/": + case " ": + case "%": + return nextMultDiv(str, i); + case "^": + return nextPower(str, i); + default: + throw '"' + oper + '" is not a valid operator.'; } }, - Namespace = function () { // instances of this will be bound to each compiled function returned to cache parsed numerical constants + Namespace = function() { // instances of this will be bound to each compiled function returned to cache parsed numerical constants var self = this; self.array = []; self.Complex = Complex; - self.addComplex = function (complex) { + self.addComplex = function(complex) { self.array.push(complex); return self.array.length - 1; // returns index of cached constant to compiler }; @@ -864,7 +1186,7 @@ Complex["2I"] = Complex(0, 2, 2, PI / 2); // Expose the Complex class - if(typeof module !== 'undefined' && module.exports) + if(typeof module !== "undefined" && module.exports) // Node.js module.exports = Complex; else diff --git a/lib/complex.min.js b/lib/complex.min.js index ac5e33c..61a3ccb 100644 --- a/lib/complex.min.js +++ b/lib/complex.min.js @@ -1 +1 @@ -(function(){function Complex(e,t,n,r){var i=this instanceof Complex,s=i?this:new Complex(e,t,n,r);if(!i)return s;s.m=typeof n=="number"?Math.abs(n):Math.sqrt(e*e+t*t);if(typeof r=="number"){s.t=n>=0?r:r+PI}else{s.t=Math.atan2(t,e)}s.t=PI-(PI*3-(PI-(PI*3-s.t)%(PI*2)))%(PI*2);s.r=e;s.i=t}var root=this,Math=root.Math;Math.sinh=function(e){return(-Math.exp(-e)+Math.exp(e))/2};Math.cosh=function(e){return(Math.exp(-e)+Math.exp(e))/2};var floor=function(e){return e>=0?e-e%1:e-(e%1+1)},round=function(e){e+=.5;return e>=0?e-e%1:e-(e%1+1)},ceil=function(e){return e>=0?e-(e%1+1):e-e%1},PI=Math.PI,g=7,index=[],p=[.9999999999998099,676.5203681218851,-1259.1392167224028,771.3234287776531,-176.6150291621406,12.507343278686905,-.13857109526572012,9984369578019572e-21,1.5056327351493116e-7].map(function(e,t){index.push(Complex(t,0,t,0));return Complex(e,0,Math.abs(e),e<0?PI:0)}),SQRT2PI=Complex(Math.sqrt(2*PI),0,Math.sqrt(2*PI),0);Complex.prototype.toString=function(e){if(!Complex.isFinite(this))return"Infinity";if(Complex.isNaN(this))return"NaN";if(e){var t=this.r.toPrecision().toUpperCase(),n=this.i.toPrecision().toUpperCase();if(this.i===0)return t;if(this.r===0)return(this.i===1?"":n+" ")+"i";return t+(this.i===1?"":this.i>0?"+"+n+" ":n+" ")+"i"}else{var r=this.m.toPrecision().toUpperCase(),i=this.t.toPrecision().toUpperCase();if(this.m===0||this.t===0)return r;return r+" e^("+i+" i)"}};Complex.exp=function(e){return e.i==0?Complex["E"].pow(e.r):Complex.Polar(Math.exp(e.r),e.i)};Complex.Polar=function(e,t){return new Complex(e*Math.cos(t),e*Math.sin(t),e,t)};Complex.prototype.add=function(e){return Complex(this.r+e.r,this.i+e.i)};Complex.prototype.sub=function(e){return Complex(this.r-e.r,this.i-e.i)};Complex.floor=function(e){return Complex(floor(e.r),floor(e.i))};Complex.ceil=function(e){return Complex(ceil(e.r),ceil(e.i))};Complex.conj=function(e){return Complex(e.r,-e.i,e.m,(PI*2-e.t)%PI*2)};Complex.prototype.mult=function(e){return Complex.Polar(this.m*e.m,this.t+e.t)};Complex.prototype.divBy=function(e){return Complex.Polar(this.m/e.m,this.t-e.t)};Complex.square=function(e){return Complex(e.r*e.r-e.i*e.i,2*e.r*e.i,e.m*e.m,e.t*2)};Complex.cube=function(e){return Complex(e.r*e.r*e.r-3*e.r*e.i*e.i,3*e.r*e.r*e.i-e.i*e.i*e.i,e.m*e.m*e.m,e.t*3)};Complex.prototype.pow=function(e){if(e==2)return Complex.square(this);if(e==3)return Complex.cube(this);return Complex.Polar(Math.pow(this.m,e),this.t*e)};Complex.sqrt=function(e){return e.pow(.5)};Complex.cbrt=function(e){return e.pow(1/3)};Complex.prototype.cPow=function(e){return e.i==0?this.pow(e.r):Complex.Polar(Math.pow(this.m,e.r)*Math.exp(-e.i*this.t),e.i*Math.log(this.m)+e.r*this.t)};Complex.gamma=function(e){if(e.r<.5)return Complex["PI"].divBy(Complex.sin(Complex["PI"].mult(e)).mult(Complex.gamma(Complex["1"].sub(e))));else{var t=e.sub(Complex["1"]),n=p[0],r;for(r=1;r=0){u=o%r;o=o-u;s=n.charAt(u)+s;o=o/r-1}i[e[a]]=s;s=""}return i},l=function(e,t){var n=1;while(e[t]=="("?n++:e[t]==")"?--n:t=0?e-e%1:e-(e%1+1)}function round(e){e+=.5;return e>=0?e-e%1:e-(e%1+1)}function ceil(e){return e>=0?e-(e%1+1):e-e%1}function Complex(e,t,n,r){var i=this instanceof Complex,s=i?this:new Complex(e,t,n,r);if(!i)return s;s.m=typeof n==="number"?Math.abs(n):Math.sqrt(e*e+t*t);if(typeof r==="number"){s.t=n>=0?r:r+PI}else{s.t=Math.atan2(t,e)}s.t=PI-(PI*3-(PI-(PI*3-s.t)%(PI*2)))%(PI*2);s.r=e;s.i=t}var root=this,Math=root.Math;Math.sinh=function(e){return(-Math.exp(-e)+Math.exp(e))/2};Math.cosh=function(e){return(Math.exp(-e)+Math.exp(e))/2};var g=7,index=[],p=[.9999999999998099,676.5203681218851,-1259.1392167224028,771.3234287776531,-176.6150291621406,12.507343278686905,-.13857109526572012,9984369578019572e-21,1.5056327351493116e-7].map(function(e,t){index.push(Complex(t,0,t,0));return Complex(e,0,Math.abs(e),e<0?PI:0)});var PI=Math.PI,SQRT2PI=Complex(Math.sqrt(2*PI),0,Math.sqrt(2*PI),0);Complex.prototype.toString=function(e){if(!Complex.isFinite(this))return"Infinity";if(Complex.isNaN(this))return"NaN";if(e){var t=this.r.toPrecision().toUpperCase(),n=this.i.toPrecision().toUpperCase();if(this.i===0)return t;if(this.r===0)return Math.abs(this.i)===1?this.i>0?"i":"-i":n+" i";return t+(this.i>0?"+":"")+(Math.abs(this.i)===1?this.i>0?"i":"-i":n+" i")}else{var r=this.m.toPrecision().toUpperCase(),i=this.t.toPrecision().toUpperCase();if(this.m===0||this.t===0)return r;return r+" e^("+i+" i)"}};Complex.exp=function(e){return e.i===0?Complex["E"].pow(e.r):Complex.Polar(Math.exp(e.r),e.i)};Complex.Polar=function(e,t){return new Complex(e*Math.cos(t),e*Math.sin(t),e,t)};Complex.prototype.add=function(e){return Complex(this.r+e.r,this.i+e.i)};Complex.prototype.sub=function(e){return Complex(this.r-e.r,this.i-e.i)};Complex.floor=function(e){return Complex(floor(e.r),floor(e.i))};Complex.ceil=function(e){return Complex(ceil(e.r),ceil(e.i))};Complex.conj=function(e){return Complex(e.r,-e.i,e.m,(PI*2-e.t)%PI*2)};Complex.prototype.mult=function(e){return Complex.Polar(this.m*e.m,this.t+e.t)};Complex.prototype.divBy=function(e){return Complex.Polar(this.m/e.m,this.t-e.t)};Complex.square=function(e){return Complex(e.r*e.r-e.i*e.i,2*e.r*e.i,e.m*e.m,e.t*2)};Complex.cube=function(e){return Complex(e.r*e.r*e.r-3*e.r*e.i*e.i,3*e.r*e.r*e.i-e.i*e.i*e.i,e.m*e.m*e.m,e.t*3)};Complex.prototype.pow=function(e){if(e===2)return Complex.square(this);if(e===3)return Complex.cube(this);return Complex.Polar(Math.pow(this.m,e),this.t*e)};Complex.sqrt=function(e){return e.pow(.5)};Complex.cbrt=function(e){return e.pow(1/3)};Complex.prototype.cPow=function(e){return e.i===0?this.pow(e.r):Complex.Polar(Math.pow(this.m,e.r)*Math.exp(-e.i*this.t),e.i*Math.log(this.m)+e.r*this.t)};Complex.gamma=function(e){if(e.r<.5)return Complex["PI"].divBy(Complex.sin(Complex["PI"].mult(e)).mult(Complex.gamma(Complex["1"].sub(e))));else{var t=e.sub(Complex["1"]),n=p[0],r;for(r=1;r=0){u=o%r;o=o-u;s=n.charAt(u)+s;o=o/r-1}i[e[a]]=s;s=""}return i},l=function(e,t){var n=1;while(e[t]==="("?n++:e[t]===")"?--n:t