var Complex = function (arg0, arg1){
	arg0 = arg0 || 0;
	arg1 = arg1 || 0;
	if (typeof arg0 == "number" && typeof arg1 == "number"){
		this.r = arg0;
		this.i = arg1;
	} else {
		var tmp = Complex.conv(arg0)
		this.r = tmp.r;
		this.i = tmp.i;
	}
};

// 加減乗除メソッドは破壊的
Complex.prototype = {
	toString: function(){return Complex.toString(this);},
	valueOf:  function(){return Complex.toString(this);},
	abs: function(){return Complex.abs(this);},
	add: function(cn){
		var tmp = Complex.add(this, cn);
		this.r = tmp.r;
		this.i = tmp.i;
		return this;
	},
	sub: function(cn){
		var tmp = Complex.sub(this, cn);
		this.r = tmp.r;
		this.i = tmp.i;
		return this;
	},
	mul: function(cn){
		var tmp = Complex.mul(this, cn);
		this.r = tmp.r;
		this.i = tmp.i;
		return this;
	},
	div: function(cn){
		var tmp = Complex.div(this, cn);
		this.r = tmp.r;
		this.i = tmp.i;
		return this;
	}
};

// 以下はインスタンスのメソッドではない
// 引数をインスタンスもどき（メソッドなしでプロパティr, iのみのオブジェクト）に変換する関数
Complex.conv = function(arg){
	if (typeof arg == "object") {
		return {r: Number(arg.r || arg[0] || 0), i: Number(arg.i || arg[1] || 0)};
	} else if (typeof arg == "string") {
		var srcNum = "([+\\-]?(?:[0-9]+\\.?[0-9]*|0?\\.[0-9]+)(?:e[+\\-]?[0-9]+)?)";
		var reCNum = new RegExp("^(?:" + srcNum + "(?!i))?(?:" + srcNum + "i)?$", "i");
		reCNum.exec(arg.replace(/\s/g, "").replace(/(^|\+|-)i/i, "$11i"));
		return {r: Number(RegExp.$1), i: Number(RegExp.$2)};
	}
	return {r: Number(arg), i: 0};
};

// インスタンス相当を引数に取る関数
Complex.toString = function(cn){
	cn = Complex.conv(cn);
	return cn.r.toString() + (cn.i >= 0 ? "+" : "") + cn.i.toString() + "i";
};
Complex.abs = function(cn){
	cn = Complex.conv(cn);
	return Math.sqrt(cn.r * cn.r + cn.i * cn.i);
};
// 加減乗除関数は新しいインスタンスを返す
Complex.add = function(cn0, cn1){
	cn0 = Complex.conv(cn0);
	cn1 = Complex.conv(cn1);
	return new Complex(cn0.r + cn1.r, cn0.i + cn1.i);
};
Complex.sub = function(cn0, cn1){
	cn0 = Complex.conv(cn0);
	cn1 = Complex.conv(cn1);
	return new Complex(cn0.r - cn1.r, cn0.i - cn1.i);
};
Complex.mul = function(cn0, cn1){
	cn0 = Complex.conv(cn0);
	cn1 = Complex.conv(cn1);
	return new Complex(cn0.r * cn1.r - cn0.i * cn1.i,
		               cn0.i * cn1.r + cn0.r * cn1.i);
};
Complex.div = function(cn0, cn1){
	cn0 = Complex.conv(cn0);
	cn1 = Complex.conv(cn1);
	var absSq = cn1.r * cn1.r + cn1.i * cn1.i;
	return new Complex((cn0.r * cn1.r + cn0.i * cn1.i) / absSq,
	                   (cn0.i * cn1.r - cn0.r * cn1.i) / absSq);
};
// 定義部はここまで

/* お題の計算。結果は以下
7+0i
3-15i
1+55i
1.1333333333333333-0.4i
3.605551275463989
*/
alert(
	         Complex.add(new Complex(3, 1),  new Complex(4, -1))
	+ "\n" + Complex.sub(new Complex(5, -9), new Complex(2, 6))
	+ "\n" + Complex.mul(new Complex(5, 3),  new Complex(5, 8))
	+ "\n" + Complex.div(new Complex(9, -7), new Complex(9, -3))
	+ "\n" + Complex.abs(new Complex(2, 3))
);

// インスタンスを作る。引数は数値2つ、
// もしくは数値要素を2つ持つ（連想）配列か、複素数と解釈できる文字列
var c0 = new Complex(0, 1);
var c1 = new Complex([1, 1]);
var c2 = new Complex({r:3, i:-4});
var c3 = new Complex("5-8i");

// 絶対値を出してみる。上3行と下3行の結果は同じ。
alert(
	         "|" + c0 + "| = " + Complex.abs(c0)
	+ "\n" + "|" + c1 + "| = " + Complex.abs(c1)
	+ "\n" + "|" + c2 + "| = " + Complex.abs(c2)
	+ "\n" + "|" + c0 + "| = " + c0.abs()
	+ "\n" + "|" + c1 + "| = " + c1.abs()
	+ "\n" + "|" + c2 + "| = " + c2.abs()
);

// 計算1。引数なら文字列のままでも可。
// 下3行の計算は破壊的（c0の値が計算結果に変わる）
alert(
	    "(" + c0 + ") + (" + c1   + ") = " + Complex.add(c0, c1)
	+ "\n(" + c0 + ") - (" + c2   + ") = " + Complex.sub(c0, c2)
	+ "\n(" + c0 + ") / (" + "-i" + ") = " + Complex.div(c0, "-i")
	+ "\n(" + c0 + ") + (" + c1   + ") = " + c0.add(c1)
	+ "\n(" + c0 + ") - (" + c2   + ") = " + c0.sub(c2)
	+ "\n(" + c0 + ") / (" + "-i" + ") = " + c0.div("-i")
);

// 計算2。iの2乗。加減乗除の組み合わせ2つ。複素数を0で割ると0/0
alert(
	  "i * i = " + Complex.mul("i", "i")

	+ "\n(3+2i) * (5-i) * (-6+2i) / (5-i) / (-6+2i) = "
	+ (new Complex("3+2i")).mul("5-i").mul("-6+2i").div("5-i").div("-6+2i")

	+ "\n( (3+2i) + (4-i) ) * 2i * -.5i - (4-i) = "
	+ Complex.add("3+2i", "4-i").mul("2i").mul("-.5i").sub("4-i")

	+ "\n(1+2i) / 0 = " + Complex.div("1+2i", "0")
);

// 引数いろいろ。falsyな値や空配列は0+0iに
var testcases = [
	"-3-8i", "5", [0, 4], [2, -8],
	"-2+i", "-2-i", {r:-2.5, i:3e-2}, "-2E8 + I",
	"0", "i", Infinity, NaN, "", {}, null, undefined
];
for (var i = 0, rslt = ""; i < testcases.length; i++){
	rslt += (typeof testcases[i]) + ": " + testcases[i]
		+ "\n\t\t" + (new Complex(testcases[i])) + "\n";
}
alert(rslt);
