diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c81643f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..61980e4 --- /dev/null +++ b/README.md @@ -0,0 +1,845 @@ +# Complex.js + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +Complex.js is a lightweight module that enables Complex mathematics +in JavaScript. It comes with every elementary function and all +mathematical operators. It also includes many utility functions and +common non-analytical functions such as the Complex conjugate, the +argument function, the absolute value function and many others. + +Lastly, but most importantly, this module contains a compiler to +parse human-readable expressions into native JavaScript functions. +The compiler, accessible from `Complex.parseFunction`, accepts an +arbitrary amount of parameters to pass to the function, specified +by their human-readable names. Example usage can be found below in +the section [`Parsing Human-Readable Expressions`](#parsing). + +Although originally written for use in the browser, it can also now +be used within [Node.js](http://nodejs.org). + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +## Overview + +* [Download](#download) +* [Functions vs. Operators](#functions-vs-operators) +* [Coordinate Notation](#coordinate-notation) +* [Parsing Human-Readable Expressions](#parsing) +* [Documentation](#documentation) + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + +## Download + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + +## Functions vs. Operators + +Functions are denoted as `Complex.staticMethod`. For example, +to evaluate the tangent of the imaginary unit, do the following: + +```js +console.log(Complex.tan(Complex(0,1))); +``` + +All functions are static, meaning that they are called directly by +the Complex namespace. Operators are non-static methods, which means +they must be called by an instance of Complex. For example, to raise +1+5i to the power of 3*e^((2/3)pi*i), do the following: + +```js +console.log(Complex(1,5).cPow(Complex.Polar(3,2/3*Math.PI))); +``` + +Notice how `cPow` is a method of a Complex instance, and not of the +namespace Complex. That's because it is an operator rather than a +function. Non-static methods are denoted as +`Complex.prototype.nonStaticMethod`. + + + +## Coordinate Notation + +Complex.js supports both cartesian and exponential notation. In order +to declare a Complex number with cartesian coordinates, you can call +the default constructor with the following arguments: + +```js +var cartesian_1_plus_5_i = Complex(1,5); +``` + +Declaring it with the `new` keyword is optional, since the +constructor detects and corrects instantiation automatically. +Optionally, you may supply the absolute value and the argument of the +Complex number as well for the 3rd and 4th parameters, though this is +not recommended. Exponential notation is supported through the +secondary Polar constructor as such: + +```js +var exponential_1_e_to_pi_i = Complex.Polar(1,Math.PI); +``` + +Note that this constructor does not support the `new` keyword and +should never be called with it, as it does so internally. + +Similarly, both notations are supported in the toString method. +Simply call `toString()` for exponential (the default), +or `toString(true)` for cartesian notation. + +These strings can be used to reconstruct the complex instances, but +that will be covered in the next section. + + + +## Parsing Human-Readable Expressions + +Complex.js also includes a compiler for human-readable expressions, +which is very useful for constructing functions callable from +JavaScript. Since it supports virtually any common notations and +fully supports order of operations, it's very easy to use. It even +normalizes implied multiplication and non-parenthetical grouping by +default. A simple use-case example is below. + +HTML: +```html + +
+ Evaluate: + +
+
+ Cartesian: + +
+
+ Exponential: + +
+ + +``` +JavaScript: +```js +var input = document.getElementById('calc'), + cart = document.getElementById('ans-cart'), + expo = document.getElementById('ans-expo'); + +input.addEventListener('change', function(){ + try { + var + //will throw an error if input is invalid + calc = Complex.parseFunction(input.value), + //evaluate the compiled function for the answer + ans = calc(); + //use the toString method + cart.innerHTML = ans.toString(true); + expo.innerHTML = ans.toString(); + } catch(error) { + //if the parser throws an error, clear outputs and alert error + cart.innerHTML = ""; + expo.innerHTML = ""; + alert(error); + } +}); +``` + +Note that the compiler creates a function rather than evaluating the +expression that is compiled immediately. The function returned is +high-performace, since it caches all real-values in the expression +so that they don't need to be re-evaluated with each call. + +The following is an example where the compiler provides parameters +for the compiled function: + +```js +// Node.js +var Complex = require("complex-js"), + param_a = Complex(5,1), + param_b = Complex(3e-5,0), + param_c = Complex(0,5), + // human-readable variable names in expression + complex_func = "a^(b+10*sin(c))", + // array of parameters for function is order-dependent + js_func = Complex.parseFunction(complex_func, ["b","a","c"]), + // how to pass parameters to compiled function + output = js_func(param_b, param_a, param_c); + +// output cartesian form as string +console.log(output.toString(true)); +``` + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +
+## Documentation + +### [Constructors](#constructors) + +* [`Complex`](#complex) +* [`Polar`](#polar) + +### [Non-Static Methods](#non-static) + +* [`toString`](#to-string) +* [`add`](#add) +* [`sub`](#sub) +* [`mult`](#mult) +* [`divBy`](#div-by) +* [`pow`](#pow) +* [`cPow`](#c-pow) +* [`mod`](#mod) + +### [Static Methods](#static) + +* [`conj`](#conj) +* [`neg`](#neg) +* [`re`](#re) +* [`im`](#im) +* [`abs`](#abs) +* [`arg`](#arg) +* [`floor`](#floor) +* [`ceil`](#ceil) +* [`round`](#round) +* [`fPart`](#f-part) + +### [Mathematical Static Methods](#math) + +* [`exp`](#exp) +* [`log`](#log) +* [`sqrt`](#sqrt) +* [`cbrt`](#cbrt) +* [`cos`](#cos) +* [`sin`](#sin) +* [`tan`](#tan) +* [`sec`](#sec) +* [`csc`](#csc) +* [`cot`](#cot) +* [`arccos`](#arccos) +* [`arcsin`](#arcsin) +* [`arctan`](#arctan) +* [`arcsec`](#arcsec) +* [`arccsc`](#arccsc) +* [`arccot`](#arccot) +* [`cosh`](#cosh) +* [`sinh`](#sinh) +* [`tanh`](#tanh) +* [`sech`](#sech) +* [`csch`](#csch) +* [`coth`](#coth) +* [`arccosh`](#arccosh) +* [`arcsinh`](#arcsinh) +* [`arctanh`](#arctanh) +* [`arcsech`](#arcsech) +* [`arccsch`](#arccsch) +* [`arccoth`](#arccoth) + +### [Misc. Static Methods](#misc) + +* [`min`](#min) +* [`max`](#max) +* [`isNaN`](#is-nan) +* [`isFinite`](#is-finite) +* [`formatFunction`](#format-function) +* [`parseFunction`](#parse-function) + +### Constants + +For convenience, but also used in many of the trigonometric methods. + +* `0` - zero +* `1` - one +* `I` - i +* `-I` - negative i +* `PI` - irrational constant "pi" +* `E` - irrational constant "e" +* `2` - two +* `2I` - two i + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + +## Constructors + + +### Complex(real, imag[, abs[, arg]]) + +The cartesian constructor for instances of the `Complex` class. +Optionally call with `new`, but not required. + +__Arguments__ + +* `real` - A `Number` specifying the real value of the Complex number. +* `imag` - A `Number` specifying the imaginary value of the Complex number. +* `abs` - An optional `Number` specifying the absolute value of the Complex number. + Not recommended unless accurately calculated. +* `arg` - An optional `Number` specifying the argument of the Complex number. + Not recommended unless accurately calculated. + + + +### Complex.Polar(abs, arg) + +The exponential constructor for instances of the `Complex` class. +**Note** Do not call this constructor with `new`. + +__Arguments__ + +* `abs` - A `Number` specifying the absolute value of the Complex number. +* `arg` - A `Number` specifying the argument of the Complex number. + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + +## Non-Static Methods + + +### Complex.prototype.toString([cartesian]) + +The `toString` method for the `Complex` class. Outputs to exponential +or cartesian form. + +__Arguments__ + +* `cartesian` - An optional Boolean specifying the output form. + If truthy, it outputs as cartesian, otherwise it outputs as exponential. + + + +### Complex.prototype.add(complex) + +Adds two Complex numbers. + +__Arguments__ + +* `complex` - An instance of the `Complex` class to add. + + + +### Complex.prototype.sub(complex) + +Subtracts a Complex number from another. + +__Arguments__ + +* `complex` - An instance of the `Complex` class to subtract. + + + +### Complex.prototype.mult(complex) + +Multiplies two Complex numbers. + +__Arguments__ + +* `complex` - An instance of the `Complex` class to multiply. + + + +### Complex.prototype.divBy(complex) + +Divides a Complex number from another. + +__Arguments__ + +* `complex` - An instance of the `Complex` class to divide by. + + + +### Complex.prototype.pow(number) + +Raises a Complex number to a real power. + +__Arguments__ + +* `number` - A `Number` to raise the Complex number to. + + + +### Complex.prototype.cPow(complex) + +Raises a Complex number to a Complex power. + +__Arguments__ + +* `complex` - An instance of the `Complex` class to raise by. + + + +### Complex.prototype.mod(complex) + +Applies a Complex Modulus to a Complex number by cartesian coordinates. + +__Arguments__ + +* `complex` - An instance of the `Complex` class for the modulus. + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + +## Static Methods + + +### Complex.conj(complex) + +Returns the conjugate of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class to conjugate. + + + +### Complex.neg(complex) + +Returns the negative of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class to negate. + + + +### Complex.re(complex) + +Returns the real component of `complex` as an instance of Complex. +The real value is stored in the real component of the returned instance. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.im(complex) + +Returns the imaginary component of `complex` as an instance of Complex. +The imaginary value is stored in the real component of the returned instance. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.abs(complex) + +Returns the absolute value of `complex`. +The absolute value is stored in the real component of the returned instance. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arg(complex) + +Returns the argument of `complex`. +The argument is stored in the real component of the returned instance. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.floor(complex) + +Rounds down the cartesian components of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.ceil(complex) + +Rounds up the cartesian components of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.round(complex) + +Rounds the cartesian components of `complex` to the nearest integers. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.fPart(complex) + +Returns the fractional parts of the cartesian coordinates in `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + +## Mathematical Static Methods + + +### Complex.exp(complex) + +Returns the exponent function of `complex`, i.e. `e^complex` + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.log(complex) + +Returns the natural logarithm of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.sqrt(complex) + +Returns the square root of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.cbrt(complex) + +Returns the cube root of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.cos(complex) + +Returns the cosine of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.sin(complex) + +Returns the sine of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.tan(complex) + +Returns the tangent of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.sec(complex) + +Returns the secant of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.csc(complex) + +Returns the cosecant of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.cot(complex) + +Returns the cotangent of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arccos(complex) + +Returns the arccosine of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arcsin(complex) + +Returns the arcsine of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arctan(complex) + +Returns the arctangent of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arcsec(complex) + +Returns the arcsecant of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arccsc(complex) + +Returns the arccosecant of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arccot(complex) + +Returns the arccotangent of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.cosh(complex) + +Returns the hyperbolic cosine of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.sinh(complex) + +Returns the hyperbolic sine of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.tanh(complex) + +Returns the hyperbolic tangent of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.sech(complex) + +Returns the hyperbolic secant of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.csch(complex) + +Returns the hyperbolic cosecant of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.coth(complex) + +Returns the hyperbolic cotangent of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arccosh(complex) + +Returns the hyperbolic arccosine of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arcsinh(complex) + +Returns the hyperbolic arcsine of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arctanh(complex) + +Returns the hyperbolic arctangent of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arcsech(complex) + +Returns the hyperbolic arcsecant of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arccsch(complex) + +Returns the hyperbolic arccosecant of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.arccoth(complex) + +Returns the hyperbolic arccotangent of `complex`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + +## Misc. Static Methods + + +### Complex.min(complex_1[, complex_2...]) + +Returns the first complex instance with the smallest absolute value. + +__Arguments__ + +* `complex_n` - An instance of the `Complex` class. + + + +### Complex.max(complex_1[, complex_2...]) + +Returns the first complex instance with the largest absolute value. + +__Arguments__ + +* `complex_n` - An instance of the `Complex` class. + + + +### Complex.isNaN(complex) + +Returns a `Boolean`; if any component of `complex` evaluates to `NaN`, +this returns `true`, otherwise `false`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.isFinite(complex) + +Returns a `Boolean`; if the absolute value of `complex` is finite, +this returns `true`, otherwise `false`. + +__Arguments__ + +* `complex` - An instance of the `Complex` class. + + + +### Complex.formatFunction(string) + +Returns a sterilized human-readable expression that can be parsed by +[`Complex.parseFunction`](#parse-function) if `string` is a valid math +expression. + +__Arguments__ + +* `string` - A human-readable `String` of a math expression to be sterilized. + + + +### Complex.parseFunction(string[, params[, skipFormat]]) + +Returns a JavaScript function bound with pre-compiled constants parsed +from the human-readable math expression `string`. Optionally, an `Array` +of human-readable parameters may be supplied to parse from the expression. +Lastly, `skipFormat` is an optional `Boolean` that can be specified if +`string` has already been formatted by [`Complex.formatFunction`](#format-function). + +__Arguments__ + +* `string` - A human-readable `String` of a math expression to be compiled. +* `params` - An optional `Array[String]` of human-readable parameters to parse. +* `skipFormat` - An optional `Boolean` to determine whether to skip pre-formatting. diff --git a/Complex.js b/lib/complex.js similarity index 67% rename from Complex.js rename to lib/complex.js index f9131fe..f6a45f5 100644 --- a/Complex.js +++ b/lib/complex.js @@ -1,31 +1,31 @@ /* -//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. +// 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(global){ +(function(root){ // Localize the global Math object - var Math = global.Math; + var Math = root.Math; // Math object helper functions for more advanced formulas Math.sinh = function(x){ @@ -51,17 +51,17 @@ // Component constructor for Complex class // r:Number (required) - // the Real part + // the Real part // i:Number (required) - // the Imaginary part + // the Imaginary part // m:Number (optional) - // the Magnitude + // the Magnitude // t:Number (optional) - // the Argument + // the Argument // Note: when calling this constructor, it // is recommended to omit the second - // two arguments, unless they have - // been accurately calculated. + // two arguments, unless they have + // been accurately calculated. function Complex(r,i,m,t){ var invoked = this instanceof Complex, self = invoked?this:new Complex(r,i,m,t); @@ -72,7 +72,7 @@ } else { self.t = Math.atan2(i, r); } - //limit argument between (-pi,pi] + // 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; @@ -81,22 +81,22 @@ // 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 + // 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)"; + return type?this.re.toPrecision().toUpperCase()+(this.i>=0?"+":"")+this.i.toPrecision().toUpperCase()+" i":this.r.toPrecision().toUpperCase()+"*e^("+this.t.toPrecision().toUpperCase()+"*i)"; }; // Exponent function Complex.exp = function(c){ - //formula: e^(a+bi) = e^a e^(bi) + // 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); }; // Polar constructor for Complex class // r:Number (required) - // the Magnitude + // the Magnitude // t:Number (required) - // the Argument + // the Argument Complex.Polar = function(r,t){ return new Complex(r*Math.cos(t), r*Math.sin(t), r, t); }; @@ -131,21 +131,21 @@ // Multiplication operator Complex.prototype.mult = function(c){ - //formula: r1 e^(t1i) * r2 e^(t2i) = r1r2 e^((t1+t2)i) + // 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){ - //formula: r1 e^(t1i) / (r2 e^(t2i)) = r1/r2 e^((t1-t2)i) + // 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 + // MUST be a Number, not a Complex Complex.prototype.pow = function(z){ - //formula: (r e^(ti))^(z) = r^z e^(tzi) + // formula: (r e^(ti))^(z) = r^z e^(tzi) return Complex.Polar(Math.pow(this.r,z),this.t*z); }; @@ -161,29 +161,29 @@ // Complex Power operator // c:Complex - // MUST be a Complex, not a Number + // MUST be a Complex, not a Number Complex.prototype.cPow = function(c){ - //formula: (r e^(ti))^(a+bi) = (r^a e^(-bt)) e^((ln(r)b+at)i) + // 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){ - //formula: cos(c) = (e^(ci)+e^(-ci))/2 + // 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){ - //formula: sin(c) = (e^(ci)-e^(-ci))/(2i) + // 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){ - //formula: tan(c) = (e^(ci)-e^(-ci))/(i(e^(ci)+e^(-ci))) + // 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)); @@ -192,21 +192,21 @@ // Secant function Complex.sec = function(c){ - //formula: sec(c) = 2/(e^(ci)+e^(-ci)) + // 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){ - //formula: csc(c) = 2i/(e^(ci)-e^(-ci)) + // 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){ - //formula: cot(c) = i(e^(ci)+e^(-ci))/(e^(ci)-e^(-ci)) + // 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)); @@ -215,123 +215,123 @@ // Natural Logarithm function Complex.log = function(c){ - //formula: log(r e^(ti)) = log(r)+i(t+2pi*floor(1/2-t/(2pi))) + // 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){ - //formula: arcsin(c) = -i*log(ic+sqrt(1-c^2)) + // 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){ - //formula: arccos(c) = i*log(c-i*sqrt(1-c^2)) + // 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){ - //formula: arctan(c) = i/2 log((i+x)/(i-x)) + // 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){ - //formula: arcsec(c) = -i*log(1/c+sqrt(1-i/c^2)) + // 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){ - //formula: arccsc(c) = -i*log(i/c+sqrt(1-1/c^2)) + // 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){ - //formula: arccot(c) = i/2 log((c-i)/(c+i)) + // formula: arccot(c) = i/2 log((c-i)/(c+i)) var i = Complex["I"]; return i.mult(Complex.log(c.sub(i).divBy(c.add(i)))).divBy(Complex["2"]); }; // Hyperbolic Sine function Complex.sinh = function(c){ - //formula: sinh(c) = (e^c-e^-c)/2 + // 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){ - //formula: cosh(c) = (e^c+e^-c)/2 + // 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){ - //formula: tanh(c) = (e^c-e^-c)/(e^c+e^-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){ - //formula: csch(c) = 2/(e^c-e^-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){ - //formula: sech(c) = 2/(e^c+e^-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){ - //formula: coth(c) = (e^c+e^-c)/(e^c-e^-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){ - //formula: arcsinh(c) = log(c+sqrt(c^2+1)) + // 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){ - //formula: arccosh(c) = log(c+sqrt(c^2-1)) + // 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){ - //formula: arctanh(c) = 1/2 log((1+c)/(1-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){ - //formula: arcsech(c) = log((1+sqrt(1-c^2))/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){ - //formula: arccsch(c) = log((1+sqrt(1+c^2))/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 + // Inverse Hyperbolic Cotangent function Complex.arccoth = function(c){ - //formula: arccoth(c) = 1/2 log((c+1)/(c-1)) + // 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,(c.t+PI2)%PI2); + return Complex(-c.re,-c.i,c.r,c.t+Math.PI); }; // Real function @@ -375,16 +375,32 @@ // Modulus operator // Returns the modulus of each component separately 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))); + return Complex(this.re%c.re,this.i%c.i); }; // Mininum function // Returns the Complex with the smallest magnitude - Complex.min = function(c1,c2){ - return Math.min(c1.r,c2.r)==c1.r?c1:c2; - }; + Complex.min = function(){ + var args = Array.prototype.slice.call( + arguments + ).map(function(c){ + return c.r; + }), + 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( + arguments + ).map(function(c){ + return c.r; + }), + max = Math.max.apply(Math, args); + return arguments[args.indexOf(max)]; + } // Determines whether the Complex is validly formed or not // Returns a Boolean @@ -399,41 +415,41 @@ }; // Complex expression parser - // str:String + // str:String (required) // human-readable Complex math expression - // args:Array[String] + // 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 + // 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){ - 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 + 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 str = str.toLowerCase(); - //default is to format the human-readable string + // default is to format the human-readable string if(!skipFormat) str = formatFunction(str); var namespace = new Namespace(); - //the constructed function being returned + // the constructed function being returned return new Function( - //arguments being mapped to compiled names + // arguments being mapped to compiled names args.map(function(arg){ return vars[arg]; }).join(","), - //function body being compiled + // 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 + lastObj = 0; // 0:function||unary operator,1:binary operator,2:variable||constant while(i=0){ - k = j%mod; //"digit" index + k = j%mod; // "digit" index j = j-k; str = abc.charAt(k) + str; - j = j/mod - 1; //leftover + j = j/mod - 1; // leftover } map[args[i]] = str; str = ""; } 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=0?r:r+Math.PI}else{s.t=Math.atan2(t,e)}s.t=Math.PI-(Math.PI*3-(Math.PI-(Math.PI*3-s.t)%PI2))%PI2;s.re=e;s.i=t}var 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};var PI2=Math.PI*2;Complex.prototype.toString=function(e){return e?this.re.toPrecision().toUpperCase()+(this.i>=0?"+":"")+this.i.toPrecision().toUpperCase()+" i":this.r.toPrecision().toUpperCase()+"*e^("+this.t.toPrecision().toUpperCase()+"*i)"};Complex.exp=function(e){return e.i==0?Complex["E"].pow(e.re):Complex.Polar(Math.exp(e.re),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.re+e.re,this.i+e.i)};Complex.prototype.sub=function(e){return Complex(this.re-e.re,this.i-e.i)};Complex.floor=function(e){return Complex(floor(e.re),floor(e.i))};Complex.ceil=function(e){return Complex(ceil(e.re),ceil(e.i))};Complex.conj=function(e){return Complex(e.re,-e.i,e.r,(PI2-e.t)%PI2)};Complex.prototype.mult=function(e){return Complex.Polar(this.r*e.r,this.t+e.t)};Complex.prototype.divBy=function(e){return Complex.Polar(this.r/e.r,this.t-e.t)};Complex.prototype.pow=function(e){return Complex.Polar(Math.pow(this.r,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.re):Complex.Polar(Math.pow(this.r,e.re)*Math.exp(-e.i*this.t),e.i*Math.log(this.r)+e.re*this.t)};Complex.cos=function(e){var t=e.mult(Complex["I"]);return Complex.exp(t).add(Complex.exp(Complex.neg(t))).divBy(Complex["2"])};Complex.sin=function(e){var t=e.mult(Complex["I"]);return Complex.exp(t).sub(Complex.exp(Complex.neg(t))).divBy(Complex["2I"])};Complex.tan=function(e){var t=e.mult(Complex["I"]),n=Complex.exp(t),r=Complex.exp(Complex.neg(t));return n.sub(r).divBy(n.add(r).mult(Complex["I"]))};Complex.sec=function(e){var t=e.mult(Complex["I"]);return Complex["2"].divBy(Complex.exp(t).add(Complex.exp(Complex.neg(t))))};Complex.csc=function(e){var t=e.mult(Complex["I"]);return Complex["2I"].divBy(Complex.exp(t).sub(Complex.exp(Complex.neg(t))))};Complex.cot=function(e){var t=e.mult(Complex["I"]),n=Complex.exp(t),r=Complex.exp(Complex.neg(t));return n.add(r).mult(Complex["I"]).divBy(n.sub(r))};Complex.log=function(e){return Complex(Math.log(e.r),e.t+PI2*round(-e.t/PI2))};Complex.arcsin=function(e){return Complex["-I"].mult(Complex.log(Complex["I"].mult(e).add(Complex.sqrt(Complex["1"].sub(e.pow(2))))))};Complex.arccos=function(e){return Complex["I"].mult(Complex.log(e.add(Complex["-I"].mult(Complex.sqrt(Complex["1"].sub(e.pow(2)))))))};Complex.arctan=function(e){var t=Complex["I"];return t.mult(Complex.log(t.add(e).divBy(t.sub(e)))).divBy(Complex["2"])};Complex.arcsec=function(e){return Complex["-I"].mult(Complex.log(e.pow(-1).add(Complex.sqrt(Complex["1"].sub(Complex["I"].divBy(e.pow(2)))))))};Complex.arccsc=function(e){return Complex["-I"].mult(Complex.log(Complex["I"].divBy(e).add(Complex.sqrt(Complex["1"].sub(e.pow(-2))))))};Complex.arccot=function(e){var t=Complex["I"];return t.mult(Complex.log(e.sub(t).divBy(e.add(t)))).divBy(Complex["2"])};Complex.sinh=function(e){return Complex.exp(e).sub(Complex.exp(Complex.neg(e))).divBy(Complex["2"])};Complex.cosh=function(e){return Complex.exp(e).add(Complex.exp(Complex.neg(e))).divBy(Complex["2"])};Complex.tanh=function(e){return Complex.exp(e).sub(Complex.exp(Complex.neg(e))).divBy(Complex.exp(e).add(Complex.exp(Complex.neg(e))))};Complex.csch=function(e){return Complex["2"].divBy(Complex.exp(e).sub(Complex.exp(Complex.neg(e))))};Complex.sech=function(e){return Complex["2"].divBy(Complex.exp(e).add(Complex.exp(Complex.neg(e))))};Complex.coth=function(e){return Complex.exp(e).add(Complex.exp(Complex.neg(e))).divBy(Complex.exp(e).sub(Complex.exp(Complex.neg(e))))};Complex.arcsinh=function(e){return Complex.log(e.add(Complex.sqrt(e.pow(2).add(Complex["1"]))))};Complex.arccosh=function(e){return Complex.log(e.add(Complex.sqrt(e.pow(2).sub(Complex["1"]))))};Complex.arctanh=function(e){return Complex.log(Complex["1"].add(e).divBy(Complex["1"].sub(e))).divBy(Complex["2"])};Complex.arcsech=function(e){return Complex.log(Complex["1"].add(Complex.sqrt(Complex["1"].sub(e.pow(2)))).divBy(e))};Complex.arccsch=function(e){return Complex.log(Complex["1"].add(Complex.sqrt(Complex["1"].add(e.pow(2)))).divBy(e))};Complex.arccoth=function(e){return Complex.log(e.add(Complex["1"]).divBy(e.sub(Complex["1"]))).divBy(Complex["2"])};Complex.neg=function(e){return Complex(-e.re,-e.i,e.r,e.t+Math.PI)};Complex.re=function(e){return Complex(e.re,0,Math.abs(e.re),e.re<0?Math.PI:0)};Complex.im=function(e){return Complex(e.i,0,Math.abs(e.i),e.i<0?Math.PI:0)};Complex.abs=function(e){return Complex(e.r,0,e.r,0)};Complex.arg=function(e){return Complex(e.t,0,e.t,0)};Complex.round=function(e){return Complex(round(e.re),round(e.i))};Complex.fPart=function(e){return Complex(e.re-floor(e.re),e.i-floor(e.i))};Complex.prototype.mod=function(e){return Complex(this.re%e.re,this.i%e.i)};Complex.min=function(){var e=Array.prototype.slice.call(arguments).map(function(e){return e.r}),t=Math.min.apply(Math,e);return arguments[e.indexOf(t)]};Complex.max=function(){var e=Array.prototype.slice.call(arguments).map(function(e){return e.r}),t=Math.max.apply(Math,e);return arguments[e.indexOf(t)]};Complex.isNaN=function(e){return isNaN(e.re)||isNaN(e.i)||isNaN(e.r)||isNaN(e.t)};Complex.isFinite=function(e){return isFinite(e.r)};Complex.parseFunction=function(str,args,skipFormat){with(this){var vars=generate(args||[]);str=str.toLowerCase();if(!skipFormat)str=formatFunction(str);var namespace=new Namespace;return(new Function(args.map(function(e){return vars[e]}).join(","),"return "+function compile(e){var t=compile,n="",r=0,i=0;while(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 - -After including that in the head, your program should be able -to use complex math quite easily (assuming that Complex.js is in -the same directory as your program). - --CREATING A NEW COMPLEX NUMBER -Simply use the Javascript command: - -var foo = new Complex(0,0); -// OR -var foo = Complex(0,0); - -...and that should create the Complex zero. Note that the following -does NOT do the same thing: - -var foo = new Number(0); - -This will NOT be able to be manipulated by the Complex library, -excluding the function "Complex.prototype.pow" which requires -a Number as the argument. - -You may also declare a Complex by its polar coordinates through -the following function: - -var foo = Complex.polar(1,Math.PI/2); - -This creates the Complex imaginary unit. Note that the declaration -omits the "new." This is because Complex.polar returns a -new Complex internally. - --FUNCTIONS VS OPERATORS -There are two main categories of mathematical manipulations in -this library, as noted by the name of this section. Functions -are a manipulation that require a single input variable, such -as: - -var tanFoo = Complex.tan(foo); -var sqrtFoo = Complex.sqrt(foo); -var conjFoo = Complex.conj(foo); - -There are many others, but to tell them apart, there is a -differece. They follow the prefix "Complex." rather than -"Complex.prototype." Operators are the opposite, and they -require two input variables, one variable is the caller and -the other is the argument. The following shows how to use -an operator: - -var twoFoo = foo.add(foo); - -Again, there are a few more, but they follow the same general -standards. - --PARSING USER INPUT -The library includes a function for parsing a mathematical -expression (generally user input) and silently fails if it -cannot parse the function correctly. Below is the documentation -for the Complex parser: - - // Complex expression parser - // str:String - // human-readable Complex math expression - // args:Array[String] - // array of arguments for the expected function, - // each index containing the name of the variable - // in the human-readable string - // skipFormat:Boolean - // 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) - -So now you can make a complex math calculator quite easily with this library! - --CONCLUSION -If you have gotten this far, congratulations. You have finished -the tutorial for this library and are now prepared to use it -in your program. \ No newline at end of file