__HEX__ = "0123456789abcdef";
function __quadHex(quad)
{
	var str = "";
	var i;
	var j;
	for (i = 0, j = 28; i < 8; i++, j -= 4)
		str += __HEX__.charAt((quad >>> j) & 0xF);
	return str;
}

/**
 * give the unicode str and get the md result
 * null str means from and end1 is 0
 * each unicode char is treated as two bytes in big-endian
 * return value is locally allocated array
 * return[0] is highest 32 bits, return[3] is lowest 32 bits
 * (((long)return[0]) << 32) | (return[1] & 0xFFFFFFFFL) is high 64 bits
 * (((long)return[2]) << 32) | (return[3] & 0xFFFFFFFFL) is low 64 bits
 * thread-safe without synchronized
 */
function unicodeMd5(str)
{
	var x = new Array(20); // x[16-19] means the md result in little-endian
	x[16] = 0x67452301;
	x[17] = 0xEFCDAB89;
	x[18] = 0x98BADCFE;
	x[19] = 0x10325476;
	var n;
	var d;
	for (n = 0, d = 0; d < str.length - 1; d += 2)
	{
		x[n++] = (str.charCodeAt(d) >>> 8) | ((str.charCodeAt(d) & 0xFF) << 8) //
			| ((str.charCodeAt(d + 1) >>> 8) << 16) | ((str.charCodeAt(d + 1) & 0xFF) << 24);
		if (n == 16)
		{
			Md5_transform(x);
			n = 0;
		}
	}
	if (d == str.length)
		x[n++] = 0x80;
	else // d == str.length - 1
		x[n++] = (str.charCodeAt(d) >>> 8) | ((str.charCodeAt(d) & 0xFF) << 8) | 0x800000;
	if (n == 15)
		x[n++] = 0;
	if (n == 16)
	{
		Md5_transform(x);
		n = 0;
	}
	if (n < 14)
		for ( ; n < 14; n++)
			x[n] = 0;
	x[14] = str.length << 4;
	x[15] = str.length >> 28;
	Md5_transform(x);
	x[0] = x[19];
	x[1] = x[18];
	x[2] = x[17];
	x[3] = x[16];
	return __quadHex(x[0]) + __quadHex(x[1]) + __quadHex(x[2]) + __quadHex(x[3]);
}

function Md5_F(x, y, z)
{
	return (x & y) | ((~x) & z);
}

function Md5_G(x, y, z)
{
	return (x & z) | (y & (~z));
}

function Md5_H(x, y, z)
{
	return x ^ y ^ z;
}

function Md5_I(x, y, z)
{
	return y ^ (x | (~z));
}

function Md5_FF(a, b, c, d, x, s, ac)
{
	a += Md5_F(b, c, d) + x + ac;
	return ((a << s) | (a >>> (32 - s))) + b;
}

function Md5_GG(a, b, c, d, x, s, ac)
{
	a += Md5_G(b, c, d) + x + ac;
	return ((a << s) | (a >>> (32 - s))) + b;
}

function Md5_HH(a, b, c, d, x, s, ac)
{
	a += Md5_H(b, c, d) + x + ac;
	return ((a << s) | (a >>> (32 - s))) + b;
}

function Md5_II(a, b, c, d, x, s, ac)
{
	a += Md5_I(b, c, d) + x + ac;
	return ((a << s) | (a >>> (32 - s))) + b;
}

function Md5_transform(x)
{
	var a = x[16], b = x[17], c = x[18], d = x[19];
	//
	// Round 1
	a = Md5_FF(a, b, c, d, x[0], 7, 0xD76AA478);
	d = Md5_FF(d, a, b, c, x[1], 12, 0xE8C7B756);
	c = Md5_FF(c, d, a, b, x[2], 17, 0x242070DB);
	b = Md5_FF(b, c, d, a, x[3], 22, 0xC1BDCEEE);
	//
	a = Md5_FF(a, b, c, d, x[4], 7, 0xF57C0FAF);
	d = Md5_FF(d, a, b, c, x[5], 12, 0x4787C62A);
	c = Md5_FF(c, d, a, b, x[6], 17, 0xA8304613);
	b = Md5_FF(b, c, d, a, x[7], 22, 0xFD469501);
	//
	a = Md5_FF(a, b, c, d, x[8], 7, 0x698098D8);
	d = Md5_FF(d, a, b, c, x[9], 12, 0x8B44F7AF);
	c = Md5_FF(c, d, a, b, x[10], 17, 0xFFFF5BB1);
	b = Md5_FF(b, c, d, a, x[11], 22, 0x895CD7BE);
	//
	a = Md5_FF(a, b, c, d, x[12], 7, 0x6B901122);
	d = Md5_FF(d, a, b, c, x[13], 12, 0xFD987193);
	c = Md5_FF(c, d, a, b, x[14], 17, 0xA679438E);
	b = Md5_FF(b, c, d, a, x[15], 22, 0x49B40821);
	//
	// Round 2
	a = Md5_GG(a, b, c, d, x[1], 5, 0xF61E2562);
	d = Md5_GG(d, a, b, c, x[6], 9, 0xC040B340);
	c = Md5_GG(c, d, a, b, x[11], 14, 0x265E5A51);
	b = Md5_GG(b, c, d, a, x[0], 20, 0xE9B6C7AA);
	//
	a = Md5_GG(a, b, c, d, x[5], 5, 0xD62F105D);
	d = Md5_GG(d, a, b, c, x[10], 9, 0x2441453);
	c = Md5_GG(c, d, a, b, x[15], 14, 0xD8A1E681);
	b = Md5_GG(b, c, d, a, x[4], 20, 0xE7D3FBC8);
	//
	a = Md5_GG(a, b, c, d, x[9], 5, 0x21E1CDE6);
	d = Md5_GG(d, a, b, c, x[14], 9, 0xC33707D6);
	c = Md5_GG(c, d, a, b, x[3], 14, 0xF4D50D87);
	b = Md5_GG(b, c, d, a, x[8], 20, 0x455A14ED);
	//
	a = Md5_GG(a, b, c, d, x[13], 5, 0xA9E3E905);
	d = Md5_GG(d, a, b, c, x[2], 9, 0xFCEFA3F8);
	c = Md5_GG(c, d, a, b, x[7], 14, 0x676F02D9);
	b = Md5_GG(b, c, d, a, x[12], 20, 0x8D2A4C8A);
	//
	// Round 3
	a = Md5_HH(a, b, c, d, x[5], 4, 0xFFFA3942);
	d = Md5_HH(d, a, b, c, x[8], 11, 0x8771F681);
	c = Md5_HH(c, d, a, b, x[11], 16, 0x6D9D6122);
	b = Md5_HH(b, c, d, a, x[14], 23, 0xFDE5380C);
	//
	a = Md5_HH(a, b, c, d, x[1], 4, 0xA4BEEA44);
	d = Md5_HH(d, a, b, c, x[4], 11, 0x4BDECFA9);
	c = Md5_HH(c, d, a, b, x[7], 16, 0xF6BB4B60);
	b = Md5_HH(b, c, d, a, x[10], 23, 0xBEBFBC70);
	//
	a = Md5_HH(a, b, c, d, x[13], 4, 0x289B7EC6);
	d = Md5_HH(d, a, b, c, x[0], 11, 0xEAA127FA);
	c = Md5_HH(c, d, a, b, x[3], 16, 0xD4EF3085);
	b = Md5_HH(b, c, d, a, x[6], 23, 0x4881D05);
	//
	a = Md5_HH(a, b, c, d, x[9], 4, 0xD9D4D039);
	d = Md5_HH(d, a, b, c, x[12], 11, 0xE6DB99E5);
	c = Md5_HH(c, d, a, b, x[15], 16, 0x1FA27CF8);
	b = Md5_HH(b, c, d, a, x[2], 23, 0xC4AC5665);
	//
	// Round 4
	a = Md5_II(a, b, c, d, x[0], 6, 0xF4292244);
	d = Md5_II(d, a, b, c, x[7], 10, 0x432AFF97);
	c = Md5_II(c, d, a, b, x[14], 15, 0xAB9423A7);
	b = Md5_II(b, c, d, a, x[5], 21, 0xFC93A039);
	//
	a = Md5_II(a, b, c, d, x[12], 6, 0x655B59C3);
	d = Md5_II(d, a, b, c, x[3], 10, 0x8F0CCC92);
	c = Md5_II(c, d, a, b, x[10], 15, 0xFFEFF47D);
	b = Md5_II(b, c, d, a, x[1], 21, 0x85845DD1);
	//
	a = Md5_II(a, b, c, d, x[8], 6, 0x6FA87E4F);
	d = Md5_II(d, a, b, c, x[15], 10, 0xFE2CE6E0);
	c = Md5_II(c, d, a, b, x[6], 15, 0xA3014314);
	b = Md5_II(b, c, d, a, x[13], 21, 0x4E0811A1);
	//
	a = Md5_II(a, b, c, d, x[4], 6, 0xF7537E82);
	d = Md5_II(d, a, b, c, x[11], 10, 0xBD3AF235);
	c = Md5_II(c, d, a, b, x[2], 15, 0x2AD7D2BB);
	b = Md5_II(b, c, d, a, x[9], 21, 0xEB86D391);
	//
	//
	x[16] += a;
	x[17] += b;
	x[18] += c;
	x[19] += d;
}
