API Docs for: 1.0.0
Show:

File: src/gallery-complexnumber/js/ComplexMath.js

/**********************************************************************
 * Support for complex numbers.
 *
 * @module gallery-complexnumber
 * @main gallery-complexnumber
 */

/**
 * This collection of functions provides the complex number equivalent of
 * the built-in JavaScript Math namespace, along with the basic arithmetic
 * operations (since JavaScript does not support operator overloading).
 * 
 * @class ComplexMath
 */

function failIfConstant(v)
{
	if (v === ComplexMath.ZERO || v === ComplexMath.I)
	{
		throw Error('You cannot modify ZERO or I');
	}
}

var ComplexMath =
{
	/**
	 * Zero.
	 * 
	 * @property ZERO
	 * @type {ComplexNumber}
	 * @static
	 * @final
	 */
	ZERO: new ComplexNumber(),

	/**
	 * Square root of -1.
	 * 
	 * @property I
	 * @type {ComplexNumber}
	 * @static
	 * @final
	 */
	I: new ComplexNumber(0,1),

	/**
	 * @method isComplexNumber
	 * @static
	 * @return {boolean} true if the argument is a ComplexNumber
	 */
	isComplexNumber: function(v)
	{
		return ((v instanceof ComplexNumber) ||
				(v.hasOwnProperty("r") && v.hasOwnProperty("i")));
	},

	/**
	 * @method add
	 * @static
	 * @return {number} sum of all the arguments (either passed separately or as an array)
	 */
	add: function()
	{
		var s = new ComplexNumber();
		Y.Array.each(arguments, function(v)
		{
			if (Y.Lang.isArray(v))
			{
				v = ComplexMath.add.apply(this, v);
			}

			s.add(v);
		});

		return s;
	},

	/**
	 * @method addReciprocals
	 * @static
	 * @return {number} sum of the reciprocals of all the arguments (either passed separately or as an array)
	 */
	addReciprocals: function()
	{
		var s = new ComplexNumber();
		Y.Array.each(arguments, function(v)
		{
			if (Y.Lang.isArray(v))
			{
				s.add(ComplexMath.addReciprocals.apply(this, v));
			}
			else
			{
				s.add(ComplexMath.divide(1,v));
			}
		});

		return s;
	},

	/**
	 * @method parallel
	 * @static
	 * @return {number} net value of N impedances in parallel (either passed separately or as an array)
	 */
	parallel: function()
	{
		return ComplexMath.divide(1, ComplexMath.addReciprocals.apply(this, arguments));
	},

	/**
	 * @method subtract
	 * @static
	 * @param v1 {number}
	 * @param v2 {number}
	 * @return {number} v1-v2
	 */
	subtract: function(v1, v2)
	{
		var c1 = ComplexMath.isComplexNumber(v1),
			c2 = ComplexMath.isComplexNumber(v2);
		if (c1 && c2)
		{
			return new ComplexNumber(v1.r-v2.r, v1.i-v2.i);
		}
		else if (c1)
		{
			return new ComplexNumber(v1.r-v2, v1.i);
		}
		else if (c2)
		{
			return new ComplexNumber(v1-v2.r, -v2.i);
		}
		else
		{
			return new ComplexNumber(v1-v2, 0);
		}
	},

	/**
	 * @method multiply
	 * @static
	 * @return {number} product of all the arguments (either passed separately or as an array)
	 */
	multiply: function()
	{
		var s = new ComplexNumber(1, 0);
		Y.Array.each(arguments, function(v)
		{
			if (Y.Lang.isArray(v))
			{
				v = ComplexMath.multiply.apply(this, v);
			}

			s.multiply(v);
		});

		return s;
	},

	/**
	 * @method divide
	 * @static
	 * @param v1 {number}
	 * @param v2 {number}
	 * @return {number} v1/v2
	 */
	divide: function(v1, v2)
	{
		var c1 = ComplexMath.isComplexNumber(v1),
			c2 = ComplexMath.isComplexNumber(v2);
		if (c1 && c2)
		{
			var d = v2.r*v2.r + v2.i*v2.i;
			return new ComplexNumber(
				(v1.r*v2.r + v1.i*v2.i)/d,
				(v1.i*v2.r - v1.r*v2.i)/d);
		}
		else if (c1)
		{
			return new ComplexNumber(v1.r/v2, v1.i/v2);
		}
		else if (c2)
		{
			var d = v2.r*v2.r + v2.i*v2.i;
			return new ComplexNumber((v1*v2.r)/d, (-v1*v2.i)/d);
		}
		else
		{
			return new ComplexNumber(v1/v2, 0);
		}
	},

	/**
	 * @method negative
	 * @static
	 * @param v {number}
	 * @return {number} negative of the argument
	 */
	negative: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			return new ComplexNumber(-v.r, -v.i);
		}
		else
		{
			return new ComplexNumber(-v, 0);
		}
	},

	/**
	 * @method abs
	 * @static
	 * @param v {number}
	 * @return {number} absolute value (magnitude) of the argument
	 */
	abs: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			return new ComplexNumber(Math.sqrt(v.r*v.r + v.i*v.i), 0);
		}
		else
		{
			return new ComplexNumber(Math.abs(v), 0);
		}
	},

	/**
	 * @method phase
	 * @static
	 * @param v {number}
	 * @return {number} phase of the argument
	 */
	phase: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			return new ComplexNumber(Math.atan2(v.i, v.r), 0);
		}
		else
		{
			return new ComplexNumber();
		}
	},

	/**
	 * @method conjugate
	 * @static
	 * @param v {number}
	 * @return {number} complex conjugate of the argument
	 */
	conjugate: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			return new ComplexNumber(v.r, -v.i);
		}
		else
		{
			return new ComplexNumber(v, 0);
		}
	},

	/**
	 * @method rotate
	 * @static
	 * @param v {number}
	 * @param a {number} angle in radians
	 * @return {number} phase of the argument
	 */
	rotate: function(v,a)
	{
		return ComplexMath.multiply(v, ComplexNumber.fromPolar(1, a));
	},

	/**
	 * @method acosh
	 * @static
	 * @param v {number}
	 * @return {number} inverse hyperbolic cosine of the argument
	 */
	acosh: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			return ComplexMath.log(
				ComplexMath.add(v,
					ComplexMath.multiply(
						ComplexMath.sqrt(new ComplexNumber(v.r+1, v.i)),
						ComplexMath.sqrt(new ComplexNumber(v.r-1, v.i)))));
		}
		else
		{
			return new ComplexNumber(Math.acosh(v), 0);
		}
	},

	/**
	 * @method asinh
	 * @static
	 * @param v {number}
	 * @return {number} inverse hyperbolic sine of the argument
	 */
	asinh: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			var v1 = ComplexMath.multiply(v,v);
			return ComplexMath.log(
				ComplexMath.add(v,
					ComplexMath.sqrt(new ComplexNumber(v1.r+1, v1.i))));
		}
		else
		{
			return new ComplexNumber(Math.asinh(v), 0);
		}
	},

	/**
	 * @method atanh
	 * @static
	 * @param v {number}
	 * @return {number} inverse hyperbolic tangent of the argument
	 */
	atanh: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			var v1 = ComplexMath.subtract(
				ComplexMath.log(new ComplexNumber(1+v.r,  v.i)),
				ComplexMath.log(new ComplexNumber(1-v.r, -v.i)));
			return new ComplexNumber(v1.r/2, v1.i/2);
		}
		else
		{
			return new ComplexNumber(Math.atanh(v), 0);
		}
	},

	/**
	 * @method cos
	 * @static
	 * @param v {number}
	 * @return {number} cosine of the argument
	 */
	cos: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			return new ComplexNumber(
				 Math.cos(v.r)*Math.cosh(v.i),
				-Math.sin(v.r)*Math.sinh(v.i));
		}
		else
		{
			return new ComplexNumber(Math.cos(v), 0);
		}
	},

	/**
	 * @method cosh
	 * @static
	 * @param v {number}
	 * @return {number} hyperbolic cosine of the argument
	 */
	cosh: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			var v1 = ComplexMath.add(
				ComplexMath.exp(v),
				ComplexMath.exp(new ComplexNumber(-v.r, -v.i)));
			return new ComplexNumber(v1.r/2, v1.i/2);
		}
		else
		{
			return new ComplexNumber(Math.cosh(v), 0);
		}
	},

	/**
	 * @method exp
	 * @static
	 * @param v {number}
	 * @return {number} e raised to the argument
	 */
	exp: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			var v1 = new ComplexNumber(Math.cos(v.i), Math.sin(v.i));
			v1.multiply(Math.exp(v.r));
			return v1;
		}
		else
		{
			return new ComplexNumber(Math.exp(v), 0);
		}
	},

	/**
	 * @method log
	 * @static
	 * @param v {number}
	 * @return {number} natural logarithm of the argument
	 */
	log: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			return new ComplexNumber(Math.log(v.magnitude()), v.phase());
		}
		else
		{
			return new ComplexNumber(Math.log(v), 0);
		}
	},

	/**
	 * @method pow
	 * @static
	 * @param v {number} value
	 * @param e {number} exponent
	 * @return {number} value raised to the exponent
	 */
	pow: function(v, e)
	{
		var c1 = ComplexMath.isComplexNumber(v);
		if ((c1 && v.r === 0 && v.i === 0) || (!c1 && v === 0))
		{
			var c2 = ComplexMath.isComplexNumber(e);
			if ((c2 && e.r === 0 && e.i === 0) || (!c2 && e === 0))
			{
				return new ComplexNumber(1);	// 0 ^ 0
			}
			else
			{
				return new ComplexNumber();		// 0 ^ x, x != 0
			}
		}
		else
		{
			return ComplexMath.exp(ComplexMath.multiply(ComplexMath.log(v), e));
		}
	},

	/**
	 * @method sin
	 * @static
	 * @param v {number}
	 * @return {number} sine of the argument
	 */
	sin: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			return new ComplexNumber(
				Math.sin(v.r)*Math.cosh(v.i),
				Math.cos(v.r)*Math.sinh(v.i));
		}
		else
		{
			return new ComplexNumber(Math.sin(v), 0);
		}
	},

	/**
	 * @method sinh
	 * @static
	 * @param v {number}
	 * @return {number} hyperbolic sine of the argument
	 */
	sinh: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			var v1 = ComplexMath.subtract(
				ComplexMath.exp(v),
				ComplexMath.exp(new ComplexNumber(-v.r, -v.i)));
			return new ComplexNumber(v1.r/2, v1.i/2);
		}
		else
		{
			return new ComplexNumber(Math.sinh(v), 0);
		}
	},

	/**
	 * @method sqrt
	 * @static
	 * @param v {number}
	 * @return {number} square root of the argument
	 */
	sqrt: function(v)
	{
		var c = ComplexMath.isComplexNumber(v);
		return ComplexNumber.fromPolar(
			Math.sqrt(c ? v.magnitude() : Math.abs(v)),
			(c ? v.phase() : v < 0 ? Math.PI : 0) / 2);
	},

	/**
	 * @method tan
	 * @static
	 * @param v {number}
	 * @return {number} tangent of the argument
	 */
	tan: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			return ComplexMath.divide(ComplexMath.sin(v), ComplexMath.cos(v));
		}
		else
		{
			return new ComplexNumber(Math.tan(v), 0);
		}
	},

	/**
	 * @method tanh
	 * @static
	 * @param v {number}
	 * @return {number} hyperbolic tangent of the argument
	 */
	tanh: function(v)
	{
		if (ComplexMath.isComplexNumber(v))
		{
			var e = ComplexMath.exp(new ComplexNumber(2*v.r, 2*v.i));
			return ComplexMath.divide(
				new ComplexNumber(e.r-1, e.i),
				new ComplexNumber(e.r+1, e.i));
		}
		else
		{
			return new ComplexNumber(Math.tanh(v), 0);
		}
	}
};

Y.ComplexMath = ComplexMath;