From b548d2e1a2a80161abd611a3e82edf73f311a52f Mon Sep 17 00:00:00 2001 From: Patrick Roberts Date: Sun, 25 May 2014 23:27:58 -0500 Subject: [PATCH] New version --- Complex.js | 775 +++++++++++++++++++++++++++++------------------------ 1 file changed, 428 insertions(+), 347 deletions(-) diff --git a/Complex.js b/Complex.js index 89545f5..f9131fe 100644 --- a/Complex.js +++ b/Complex.js @@ -9,9 +9,9 @@ //Software, and to permit persons to whom the Software is furnished //to do so, subject to the following conditions: // -//The above copyright notice and this permission notice shall be //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 @@ -22,30 +22,30 @@ //OTHER DEALINGS IN THE SOFTWARE. */ -(function (global) { +(function(global){ // Localize the global Math object var Math = global.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); - }; + 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); + }; var PI2 = Math.PI*2; @@ -62,31 +62,33 @@ // is recommended to omit the second // two arguments, unless they have // been accurately calculated. - var Complex = function Complex (r,i,m,t) { + function Complex(r,i,m,t){ var invoked = this instanceof Complex, - self = invoked?this:new Complex; + self = invoked?this:new Complex(r,i,m,t); + if(!invoked) return self; self.r = (typeof m==="number"?Math.abs(m):Math.sqrt(r*r+i*i)); - if(typeof t==="number") { - self.t = (m>=0?t:t+Math.PI)%PI2; - self.t += (self.t>=0?0:PI2); + if(typeof t==="number"){ + self.t = (m>=0?t:t+Math.PI); } else { self.t = Math.atan2(i, r); } + //limit argument between (-pi,pi] + self.t = Math.PI-(Math.PI*3 - (Math.PI-(Math.PI*3 - self.t)%PI2))%PI2; self.re = r; self.i = i; - if(!invoked) return self; - }; + } // Use for displaying Complex numbers as a string // type:Boolean // if true, the display is a+bi form - // if false, the display is r e^(ti) form - Complex.prototype.toString = function (type) { + // if false or omitted, the display is r e^(ti) form + Complex.prototype.toString = function(type){ return type?this.re.toPrecision().toUpperCase()+"+"+this.i.toPrecision().toUpperCase()+" i":this.r.toPrecision().toUpperCase()+" e^("+this.t.toPrecision().toUpperCase()+" i)"; }; // Exponent function - Complex.exp = function (c) { + Complex.exp = function(c){ + //formula: e^(a+bi) = e^a e^(bi) return c.i==0?Complex["E"].pow(c.re):Complex.Polar(Math.exp(c.re),c.i); }; @@ -95,212 +97,246 @@ // the Magnitude // t:Number (required) // the Argument - Complex.Polar = function (r,t) { - var self = new Complex; //initializes new instance of Complex without invoking the constructor - self.re = r*Math.cos(t) - self.i = r*Math.sin(t); - self.r = Math.abs(r); - self.t = (r>=0?t:t+Math.PI)%PI2; - self.t += (self.t>=0?0:PI2); - return self; + 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) { + Complex.prototype.add = function(c){ return Complex(this.re+c.re,this.i+c.i); }; // Subtraction operator - Complex.prototype.sub = function (c) { + Complex.prototype.sub = function(c){ return Complex(this.re-c.re,this.i-c.i); }; // Floor function // integerizes the two components separately - Complex.floor = function (c) { + Complex.floor = function(c){ return Complex(floor(c.re),floor(c.i)); }; // Ceiling function // Rounds up the two compenents separately - Complex.ceil = function (c) { + Complex.ceil = function(c){ return Complex(ceil(c.re),ceil(c.i)); }; // Conjugate function // Reverses the sign of the imaginary component - Complex.conj = function (c) { - return Complex(c.re,-c.i,c.r); + Complex.conj = function(c){ + return Complex(c.re,-c.i,c.r,(PI2-c.t)%PI2); }; // Multiplication operator - Complex.prototype.mult = function (c) { + Complex.prototype.mult = function(c){ + //formula: r1 e^(t1i) * r2 e^(t2i) = r1r2 e^((t1+t2)i) return Complex.Polar(this.r*c.r,this.t+c.t); }; // Division operator - Complex.prototype.divBy = function (c) { + Complex.prototype.divBy = function(c){ + //formula: r1 e^(t1i) / (r2 e^(t2i)) = r1/r2 e^((t1-t2)i) return Complex.Polar(this.r/c.r,this.t-c.t); }; // Real Power operator // z:Number // MUST be a Number, not a Complex - Complex.prototype.pow = function (z) { + Complex.prototype.pow = function(z){ + //formula: (r e^(ti))^(z) = r^z e^(tzi) return Complex.Polar(Math.pow(this.r,z),this.t*z); }; // Square Root function - Complex.sqrt = function (c) { + Complex.sqrt = function(c){ return c.pow(.5); }; + // Cube Root function + 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) { - return c.i==0?this.pow(c.re):Complex.Polar(Math.pow(this.r*this.r,c.r*Math.cos(c.t)/2)*Math.exp(-c.r*Math.sin(c.t)*this.t),c.r*Math.sin(c.t)*Math.log(this.r*this.r)/2+c.r*Math.cos(c.t)*this.t); + 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.re):Complex.Polar(Math.pow(this.r,c.re)*Math.exp(-c.i*this.t),c.i*Math.log(this.r)+c.re*this.t); }; // Cosine function - Complex.cos = function (c) { - var i = Complex["I"]; - return Complex.exp(i.mult(c)).add(Complex.exp(Complex["-I"].mult(c))).divBy(Complex["2"]); + Complex.cos = function(c){ + //formula: cos(c) = (e^(ci)+e^(-ci))/2 + var ci = c.mult(Complex["I"]); + return Complex.exp(ci).add(Complex.exp(Complex.neg(ci))).divBy(Complex["2"]); }; // Sine function - Complex.sin = function (c) { - var i = Complex["I"]; - return Complex.exp(i.mult(c)).sub(Complex.exp(Complex["-I"].mult(c))).divBy(Complex["2I"]); + Complex.sin = function(c){ + //formula: sin(c) = (e^(ci)-e^(-ci))/(2i) + var ci = c.mult(Complex["I"]); + return Complex.exp(ci).sub(Complex.exp(Complex.neg(ci))).divBy(Complex["2I"]); }; // Tangent function - Complex.tan = function (c) { - return Complex.sin(c).divBy(Complex.cos(c)); + 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), + nex = Complex.exp(Complex.neg(ci)); + return pex.sub(nex).divBy(pex.add(nex).mult(Complex["I"])); }; // Secant function - Complex.sec = function (c) { - return Complex.cos(c).pow(-1); + 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) { - return Complex.sin(c).pow(-1); + 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) { - return Complex.tan(c).pow(-1); + 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), + nex = Complex.exp(Complex.neg(ci)); + return pex.add(nex).mult(Complex["I"]).divBy(pex.sub(nex)); }; // Natural Logarithm function - Complex.log = function (c) { + Complex.log = function(c){ + //formula: log(r e^(ti)) = log(r)+i(t+2pi*floor(1/2-t/(2pi))) return Complex(Math.log(c.r),c.t+PI2*round(-c.t/PI2)); }; // Inverse Sine function - Complex.arcsin = function (c) { - var i = Complex["I"] - return i.mult(Complex.log(Complex["1"].sub(c.pow(2)).pow(.5).sub(i.mult(c)))); + 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) { - var i = Complex["I"]; - return i.mult(Complex.log(i.mult(Complex["1"].sub(c.pow(2)).pow(.5)).sub(c))); + 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) { + 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).divBy(i.sub(c)))).divBy(Complex["2"]); }; // Inverse Secant function - Complex.arcsec = function (c) { - return Complex.arccos(c.pow(-1)); + 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) { - return Complex.arcsin(c.pow(-1)); + 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) { + Complex.arccot = function(c){ + //formula: arccot(c) = i/2 log((c-i)/(c+i)) var i = Complex["I"]; - return i.mult(Complex.log(c.add(i).divBy(c.sub(i)))).divBy(Complex["2"]); + return i.mult(Complex.log(c.sub(i).divBy(c.add(i)))).divBy(Complex["2"]); }; // Hyperbolic Sine function - Complex.sinh = function (c) { + 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) { + 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) { - return Complex.sinh(c).divBy(Complex.cosh(c)); + Complex.tanh = function(c){ + //formula: tanh(c) = (e^c-e^-c)/(e^c+e^-c) + return Complex.exp(c).sub(Complex.exp(Complex.neg(c))).divBy(Complex.exp(c).add(Complex.exp(Complex.neg(c)))); }; // Hyperbolic Cosecant function - Complex.csch = function (c) { - return Complex.sinh(c).pow(-1); + 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 - Complex.sech = function (c) { - return Complex.cosh(c).pow(-1); + 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) { - return Complex.tanh(c).pow(-1); + Complex.coth = function(c){ + //formula: coth(c) = (e^c+e^-c)/(e^c-e^-c) + return Complex.exp(c).add(Complex.exp(Complex.neg(c))).divBy(Complex.exp(c).sub(Complex.exp(Complex.neg(c)))); }; // Inverse Hyperbolic Sine function - Complex.arcsinh = function (c) { + 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) { + 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) { + 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) { - return Complex.arccosh(c.pow(-1)); + 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) { - return Complex.arcsinh(c.pow(-1)); + 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) { - return Complex.arctanh(c.pow(-1)); + 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) { - return Complex(-c.re,-c.i,c.r); + Complex.neg = function(c){ + return Complex(-c.re,-c.i,c.r,(c.t+PI2)%PI2); }; // Real function // Returns the real value as a Complex - Complex.re = function (c) { + Complex.re = function(c){ return Complex(c.re,0,Math.abs(c.re),c.re<0?Math.PI:0); }; @@ -308,48 +344,60 @@ // Returns the imaginary value as a Complex // Note that this function stores the // imaginary value in the real component - Complex.im = function (c) { + Complex.im = function(c){ return Complex(c.i,0,Math.abs(c.i),c.i<0?Math.PI:0); }; // Absolute value function // Returns the absolute value as a Complex - Complex.abs = function (c) { + Complex.abs = function(c){ return Complex(c.r,0,c.r,0); }; // Argument function // Returns the argument as a Complex - Complex.arg = function (c) { + Complex.arg = function(c){ return Complex(c.t,0,c.t,0); }; // Round function // Rounds the components separately - Complex.round = function (c) { + Complex.round = function(c){ return Complex(round(c.re),round(c.i)); }; // Fractional Part function // Returns the fractional part of each component separately - Complex.fPart = function (c) { + Complex.fPart = function(c){ return Complex(c.re-floor(c.re),c.i-floor(c.i)); }; // Modulus operator // Returns the modulus of each component separately - Complex.prototype.mod = function (c) { - var modR = this.re%c.re; - var modI = this.i%c.i; - return Complex((modR>=0?modR:modR+Math.abs(c.re)), (modI>=0?modI:modI+Math.abs(c.i))); + Complex.prototype.mod = function(c){ + var modR = this.re%c.re, + modI = this.i%c.i; + return Complex((modR>=0?modR:modR+Math.abs(c.re)),(modI>=0?modI:modI+Math.abs(c.i))); }; // Mininum function // Returns the Complex with the smallest magnitude - Complex.min = function (c1,c2) { + Complex.min = function(c1,c2){ return Math.min(c1.r,c2.r)==c1.r?c1:c2; }; + // Determines whether the Complex is validly formed or not + // Returns a Boolean + Complex.isNaN = function(c){ + return isNaN(c.re)||isNaN(c.i)||isNaN(c.r)||isNaN(c.t); + }; + + // Determines whether the Complex is finite or not + // Returns a Boolean + Complex.isFinite = function(c){ + return isFinite(c.r); + }; + // Complex expression parser // str:String // human-readable Complex math expression @@ -362,8 +410,8 @@ // 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) { - with (this) { //set the scope to the context (see below for included variables) + 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); //make everything lowercase because it's easier @@ -371,273 +419,306 @@ //default is to format the human-readable string if(!skipFormat) str = formatFunction(str); var namespace = new Namespace(); - return new Function (args.map(function (arg) { - return vars[arg]; - }).join(","), "return "+(function compile (exp) { - var self = compile, + //the constructed function being returned + return new Function( + //arguments being mapped to compiled names + args.map(function(arg){ + return vars[arg]; + }).join(","), + //function body being compiled + "return "+(function compile(exp){ + var self = compile, str = "", i = 0, lastObj = 0; //0:function||unary operator,1:binary operator,2:variable||constant - while(i=mod) - map[args[i]] = str; - str = ""; + 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 ( + operExp = /[\+\-\*\/\^ %]/, //matches +,-,*,/,^, ,% + numsExp = /(?:(?:\d*\.\d+|\d+\.\d*)|\d+)(?:e(?:-|)\d+|)/, //matches any positive float + funcObj = { //maps functions and unary operators to compiled code + "cos(": "Complex.cos(", + "sin(": "Complex.sin(", + "tan(": "Complex.tan(", + "sec(": "Complex.sec(", + "csc(": "Complex.csc(", + "cot(": "Complex.cot(", + "log(": "Complex.log(", + "ln(": "Complex.log(", + "(": "(", + "e^": "Complex.exp(", + "e^(": "Complex.exp(", + "arccos(": "Complex.arccos(", + "arcsin(": "Complex.arcsin(", + "arctan(": "Complex.arctan(", + "arcsec(": "Complex.arcsec(", + "arccsc(": "Complex.arccsc(", + "arccot(": "Complex.arccot(", + "acos(": "Complex.arccos(", + "asin(": "Complex.arcsin(", + "atan(": "Complex.arctan(", + "asec(": "Complex.arcsec(", + "acsc(": "Complex.arccsc(", + "acot(": "Complex.arccot(", + "-": "Complex.neg(", + "-(": "Complex.neg(", + "cosh(": "Complex.cosh(", + "sinh(": "Complex.sinh(", + "tanh(": "Complex.tanh(", + "sech(": "Complex.sech(", + "csch(": "Complex.csch(", + "coth(": "Complex.coth(", + "re(": "Complex.re(", + "im(": "Complex.im(", + "abs(": "Complex.abs(", + "arg(": "Complex.arg(", + "conj(": "Complex.conj(", + "int(": "Complex.floor(", + "floor(": "Complex.floor(", + "ceil(": "Complex.ceil(", + "round(": "Complex.round(", + "fpart(": "Complex.fPart(", + "ipart(": "Complex.floor(", + "sqrt(": "Complex.sqrt(", + "cbrt(": "Complex.cbrt(", + "arccosh(": "Complex.arccosh(", + "arcsinh(": "Complex.arcsinh(", + "arctanh(": "Complex.arctanh(", + "arcsech(": "Complex.arcsech(", + "arccsch(": "Complex.arccsch(", + "arccoth(": "Complex.arccoth(", + "arcosh(": "Complex.arccosh(", + "arsinh(": "Complex.arcsinh(", + "artanh(": "Complex.arctanh(", + "arsech(": "Complex.arcsech(", + "arcsch(": "Complex.arccsch(", + "arcoth(": "Complex.arccoth(", + "acosh(": "Complex.arccosh(", + "asinh(": "Complex.arcsinh(", + "atanh(": "Complex.arctanh(", + "asech(": "Complex.arcsech(", + "acsch(": "Complex.arccsch(", + "acoth(": "Complex.arccoth(", + "exp(": "Complex.exp(" + }, + operObj = { //maps binary operators to compiled code + "+": ".add(", + "-": ".sub(", + "*": ".mult(", + " ": ".mult(", + "/": ".divBy(", + "^": ".cPow(", + "%": ".mod(" + }, + varsObj = { //maps default variables to compiled code + "e": "Complex.E", + "i": "Complex.I", + "pi": "Complex.PI" + }, + formatFunction = function(str){ //optionally called before compilation in order to repair unsupported formatting + var open = /\{|\[/, + close = /\}|\]/, + //catches implied multiplication like "3i" or ")x" + //does NOT edit format "x(" where x is a-z + //because that form implies a function, not a multiplication + mult = /\d[a-z\{\[\(]|[\}\]\)][a-z\d]/i, + expo = /^\de(?:-|)\d+/i, + trim = /[\+\-\*\/\^%]/; + return str.replace(formExp, function parse(match,offset){ + if(mult.test(match)){ + //catches exponentially represented floats and omits formatting these + if(expo.test(str.slice(offset))) return match; + //recursively calls parser in case of {,[,},] + return match.charAt(0).replace(formExp, parse)+"*"+match.charAt(1).replace(formExp, parse); + } else if(open.test(match)){ + return "("; + } else if(close.test(match)){ + return ")"; + } else { + var i = match.search(trim); + return match.charAt(i!=-1?i:"*"); } - return map; - }, - endNest = function (str, i) { //returns last index of grouped expression located at i - var nest = 1; - while(str[i]=="("?nest++:(str[i]==")"?--nest:i=0){ + k = j%mod; //"digit" index + j = j-k; + str = abc.charAt(k) + str; + j = j/mod - 1; //leftover } - return str.length; - }, - 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