////////////////////////////////////////////////////////////////////////////
//
//                       Copyright (c) 2005-2008
//                       Future Team Aps 
//                       Denmark
//
//                       All Rights Reserved
//
//   This source file is subject to the terms and conditions of the
//   Future Team Software License Agreement which restricts the manner
//   in which it may be used.
//   Mail: hve@hvks.com
//
/////////////////////////////////////////////////////////////////////////////
//
// Module name     :   complexbase.js
// Module ID Nbr   :   
// Description     :   Javascript complex base class
// --------------------------------------------------------------------------
// Change Record   :   
//
// Version	Author/Date		Description of changes
// -------  ---------------	----------------------
// 01.01	HVE/2005-Oct-14	Initial release
// 01.02	HVE/2006-Oct-14	Fixed a syntax error in toFixed
// 01.03	HVE/2006-Oct-30	Add Complex Trigonometric and Complex hyberbolic functions
// 01.04	HVE/2008-Jul-18	Support of FireFox 3
// 01.05	HVE/2008-Sep-5	Support Google Chrome browser
// 01.06	HVE/2009-Jun-20 Added support for Safari by better detecting when invoke as a constructor or Conversion function
// End of Change Record
//
/////////////////////////////////////////////////////////////////////////////

// Function argument checkking 
//function check(args,func_name)
// {var actual=args.length; var expected=args.callee.length;
//  if(actual!=expected) throw Error("Wrong number of arguments in function: "+func_name+"; Expected: "+expected+"; Actually passed "+actual); }
 
 // New Complex Object
// Class Constructor. accept 2,1 and zero arguments. Guarantee that x,y is valid numbers (not undefined). 
// Can also be invoke as a conversion to Complex instead of as an constructor through the new operator
//var cntdown=10;
function Complex()
	{//var s=new String(this.constructor)+""; 
	//var sgoogle=new String("function DOMWindow() { [native code] }"); 
	//alert("s==s2:"+(s==s2)+":"+s+":"+s2+":");
	//var f=String(this.constructor).substring(0,18)!="function Complex()";
	//if(--cntdown>0) alert("In Complex():"+String(this.constructor).substring(0,18)+":"+" typeof="+typeof this.constructor+":"+f+"\n");
	if(String(this.constructor).substring(0,18)!="function Complex()") //||typeof this.constructor!="function")
	//if(typeof this.constructor!="function"||this.constructor=='function DOMWindow() { [native code] }') // Invoke as a Conversion function
		{//alert("Conversion");
		if(typeof arguments[0]=="object" && arguments[0].constructor==Complex ) return arguments[0]; 
		else if(arguments.length>=2) return new Complex(arguments[0],arguments[1]);
		 else if(arguments.length==1) return new Complex(arguments[0]); 
		 else return new Complex();
		} 
	// Invoke as an new constructor function
	if(arguments.length>=2){this.x=Number(arguments[0]); this.y=Number(arguments[1]);}
	else if(arguments.length==1){this.x=Number(arguments[0]);this.y=0;}	else {this.x=0;this.y=0;}

	if(this.x==undefined) this.x=0; if(this.y==undefined) this.y=0;
	}
	
// Class Prototypes
Complex.prototype.norm=function() { return this.x*this.x+this.y*this.y; }
Complex.prototype.negate=function() { return new Complex(-this.x,-this.y); }
Complex.prototype.toString=function()
		{ if(arguments.length<1) return "("+this.x.toString()+(this.y<0?"-i":"+i")+Math.abs(this.y).toString()+")";
		  else return "("+this.x.toString(arguments[0])+(this.y<0?"-i":"+i")+Math.abs(this.y).toString(arguments[0])+")";										
	   }
Complex.prototype.toFixed=function()
		{ if(arguments.length<1) return "("+this.x.toFixed()+(this.y<0?"-i":"+i")+Math.abs(this.y).toFixed()+")"; 
		  else return "("+this.x.toFixed(arguments[0])+(this.y<0?"-i":"+i")+Math.abs(this.y).toFixed(arguments[0])+")";
		}
Complex.prototype.toExponential=function()
		{ if(arguments.length<1) return "("+this.x.toExponential()+(this.y<0?"-i":"+i")+Math.abs(this.y).toExponential()+")";
		  else return "("+this.x.toExponential(arguments[0])+(this.y<0?"-i":"+i")+Math.abs(this.y).toExponential(arguments[0])+")";
  		}
Complex.prototype.toPrecision=function()
		{ if(arguments.length<1) return "("+this.x.toPrecision()+(this.y<0?"-i":"+i")+Math.abs(this.y).toPrecision()+")";
		  else return "("+this.x.toPrecision(arguments[0])+(this.y<0?"-i":"+i")+Math.abs(this.y).toPrecision(arguments[0])+")";
		}
Complex.prototype.toStringShort=function() {if(this.y==0)return this.x.toString(); else return "("+this.x+(this.y<0?"-i":"+i")+Math.abs(this.y)+")"; }
Complex.prototype.valueOf=function() { return this.x; }
Complex.prototype.conj=function() { return new Complex(this.x,-this.y); }
Complex.prototype.real=function() { return this.x; }
Complex.prototype.imag=function() { return this.y; }
Complex.prototype.arg=function() { return Math.atan2(this.y,this.x); }
Complex.prototype.abs=function() { if(Math.abs(this.x)>Math.abs(this.y)) return Math.abs(this.x)*Math.sqrt(1+(this.y/this.x)*(this.y/this.x)); else return Math.abs(this.y)*Math.sqrt(1+(this.x/this.y)*(this.x/this.y));}
// Class Methods
Complex.add=function(a,b) { return new Complex(a.x+b.x,a.y+b.y); }
Complex.sub=function(a,b) { return new Complex(a.x-b.x,a.y-b.y); }
Complex.mul=function(a,b) { return new Complex(a.x*b.x-a.y*b.y,a.y*b.x+a.x*b.y); } 
Complex.div=function(a,b)
		{ if(Math.abs(b.x)>=Math.abs(b.y)) {var r=b.y/b.x; var s=b.x+r*b.y; return new Complex((a.x+a.y*r)/s,(a.y-a.x*r)/s);}
		  else {var r=b.x/b.y; var s=b.y+r*b.x; return new Complex((a.x*r+a.y)/s,(a.y*r-a.x)/s);}
		}
Complex.sqrt=function(a)
		{ if(a.x>0) {var aux= a.abs()+a.x; return new Complex(Math.sqrt(aux/2),a.y/Math.sqrt(2*aux));}
		  else {var aux=a.abs()-a.x; if(a.y<0) return new Complex(Math.abs(a.y)/Math.sqrt(2*aux),-Math.sqrt(aux/2)); else return new Complex(Math.abs(a.y)/Math.sqrt(2*aux),Math.sqrt(aux/2));}
    	}
Complex.equal=function(a,b) { if(a.x==b.x&&a.y==b.y) return true; else return false; }
Complex.polar=function(r,theta) { return new Complex( r*Math.cos(theta),r*Math.sin(theta)); }
// Transcendental functions
Complex.log=function(a) { return new Complex(Math.log(a.abs()),a.arg()); }
Complex.log10=function(a) { return Complex.div(Complex.log(a),new Complex(Math.LN10)); }
Complex.exp=function(a) { return new Complex(Math.exp(a.real())*Math.cos(a.imag()),Math.exp(a.real())*Math.sin(a.imag())); }
Complex.pow=function(a,b) { return Complex.exp(Complex.mul(Complex.log(a),b)); }
// Trigonometric functions
// Add Hyperbolic function for real artguments
Math.sinh=function(x) {return (Math.exp(x)-Math.exp(-x))*0.5;}
Math.cosh=function(x) {return (Math.exp(x)+Math.exp(-x))*0.5;}
Math.tanh=function(x) {return (Math.exp(x)-Math.exp(-x))/(Math.exp(x)+Math.exp(-x));}
Complex.sin=function(t,u) { return new Complex(Math.sin(t)*Math.cosh(u),Math.cos(t)*Math.sinh(u)); }
Complex.cos=function(t,u) { return new Complex(Math.cos(t)*Math.cosh(u),-Math.sin(t)*Math.sinh(u)); }
Complex.tan=function(t,u) { var s=Math.cos(2*t)+Math.cosh(2*u); return new Complex(Math.sin(2*t)/s,Math.sinh(2*u)/s); }
Complex.sinh=function(x) { return new Complex(Math.sinh(x.real())*Math.cos(x.imag()),Math.cosh(x.real())*Math.sin(x.imag())); }
Complex.cosh=function(x) { return new Complex(Math.cosh(x.real())*Math.cos(x.imag()),Math.sinh(x.real())*Math.sin(x.imag())); }
Complex.tanh=function(x) { var s=Math.cosh(2*x.real())+Math.cos(2*x.imag()); return new Complex(Math.sinh(2*x.real())/s,Math.sin(2*x.imag())/s); }

// Class properties
Complex.zero = new Complex();
Complex.one = new Complex(1);
Complex.i= new Complex(0,1);		

// Miscellaneous parseComplex() function in it's various format: (float [+-]i float) or (float) or ([+-]i float)
// It also allows input without paranthese e.g. float [+-]i float, float, [+-]i float
function parseComplex(s)
	{var split_re=/^[\(]?([+-eE\.\d]*)([+-][iI][eE+-\.\d]+)?[\)]?$/;
	s=s.replace(/[\s]+/g,"");  // Remove all white spaces
	if(split_re.test(s)==true) // Can it be split into real and imaginary float numbers?
		{var rs, is, real, imag;
		rs=RegExp.$1; is=RegExp.$2;
		if(rs=="") real=0; else real=parseFloat(rs);
		if(is!="") is=is.replace(/[iI]/,""); // Remove the [iI] 
		if(is=="") imag=0; else imag=parseFloat(is);
//		alert("R="+rs+" I="+is+" ("+real+","+imag+")");
		return new Complex(real,imag);
		}
	else
		return new Complex(NaN,NaN);
	}
