/* vim: ts=4 sw=4 sts=4 fenc=utf-8 ff=unix :
 * =============================================================================
 *  escape
 *  Copyright (c)  2005 - 2009 kei-n
 *  URL : http://sys0tem.homelinux.com/
 * =============================================================================
 */
module sal.util.escape;

private
{
	import std.string;
	import std.conv;
	import std.ctype;

	import sal.util.number;
}


/**
 * 指定された文字列中のエスケープされている文字列を,
 * 適切な文字列に変換します.
 *
 * Copy-on-Write にて実装しています.
 *
 * Params:
 *  s = 変換する文字列
 * Return:
 *  変換後の文字列
 */
public
string toNative(string s)
{
	int pos = indexOf(s, '\\');
	if (pos < 0) { return s; }

	char[] res = s[0 .. pos].dup;
	while (pos < s.length)
	{
		pos = toNative(res, s, pos);
		pos++;
	}
	return cast(string) res;
}
unittest
{
	assert(toNative("\\\\")                      == "\\");
	assert(toNative("\\n")                       == "\n");
	assert(toNative("\\t\\r\\y")                 == "\t\r\\y");
	assert(toNative("\\y\\f")                    == "\\y\f");
	assert(toNative("ABC@")                      == "ABC@");
	assert(toNative("A\\tB\\C@")                 == "A\tB\\C@");
	assert(toNative("\\v\\a\\b\\f\\?\\'\\\"")    == "\v\a\b\f?'\"");

	// test for toNativeX
	assert(toNative("A\\x42C\\x44")              == "ABCD");
	assert(toNative("\\xxx")                     == "\\xxx");
	assert(toNative("\\x")                       == "\\x");

	// test for toNativeU
	assert(toNative("A\\u0042C\\u0044")          == "ABCD");
	assert(toNative("\\u6f22\\u5b57")            == "漢字");
	assert(toNative("\\uxxxx")                   == "\\uxxxx");
	assert(toNative("\\uxxx")                    == "\\uxxx");
	assert(toNative("\\uxx")                     == "\\uxx");
	assert(toNative("\\ux")                      == "\\ux");
	assert(toNative("\\u")                       == "\\u");

	// test for toNativeUU
	assert(toNative("A\\U00000042C\\U00000044")  == "ABCD");
	assert(toNative("\\U00006f22\\U00005b57")    == "漢字");
	assert(toNative("\\Uxxxxxxxx")               == "\\Uxxxxxxxx");
	assert(toNative("\\Uxxxx")                   == "\\Uxxxx");
	assert(toNative("\\U")                       == "\\U");

	// test for toNativeAmp
	assert(toNative("ABC\\&amp;EF")              == "ABC&EF");
	assert(toNative("ABC\\&xyz;")                == "ABC\\&xyz;");
	assert(toNative("ABC\\&amp")                 == "ABC\\&amp");
	assert(toNative("ABC\\&")                    == "ABC\\&");

	// test for toNativeDigit
	assert(toNative("\\")                        == "\\");
	assert(toNative("\\7")                       == "\7");
	assert(toNative("A\\102C\\D\\77E\\8F")       == "ABC\\D\77E\\8F");
}


/**
 * 指定された文字列中の制御文字をエスケープ文字に変換します.
 *
 * Copy-on-Write にて実装しています.
 *
 * Params:
 *  s = 変換する文字列
 * Return:
 *  変換後の文字列
 */
public
string toEscape(string s)
{
	int pos = indexOfCntrl(s);
	if (pos < 0)
	{	// 制御文字が含まれない.
		return s;
	}
	char[] res = s[0 .. pos].dup;
	while (pos < s.length)
	{
		pos = toEscape(res, s, pos);
		pos++;
	}
	return cast(string) res;
}
unittest
{
	assert(toEscape("\\")                        == "\\\\");
	assert(toEscape("\n")                        == "\\n");
	assert(toEscape("\v\a\b\f\?\'\"")            == "\\v\\a\\b\\f\\?\\'\\\"");
	assert(toEscape("\r\tXYZ")                   == "\\r\\tXYZ");
	assert(toEscape("ABC@")                      == "ABC@");
	assert(toEscape("\x00")                      == "\\x00");
	assert(toEscape("AB\nC@")                    == "AB\\nC@");
}


/**
 * 指定された文字列より, 制御文字列の位置を検索します.
 * 見つからない場合は -1 を返します.
 *
 * Params:
 *  s = 検索対象
 * Return:
 *  インデックス
 */
private
int indexOfCntrl(string s)
{
	for (int i = 0; i < s.length; i++)
	{
		if (isCntrl(s[i]))
		{
			return i;
		}
	}
	return -1;
}


/**
 * 指定された文字が制御文字, または,
 * '\\', '\'', '\"', '\?' のいずれかであるか判定します.
 * 
 * Params:
 *  c = 判定する文字
 * Return:
 *  true/false (制御文字 or \'"?/以外)
 */
private
bool isCntrl(char c)
{
	int check = iscntrl(c);
	if (check)
	{
		return true;
	}

	check = indexOf(CNTRL_CHARS, c);
	if (check < 0)
	{
		return false;
	}
	return true;
}


/**
 * 指定された8進数文字を数値に変換します.
 * 8進数文字でない場合 -1 を返します.
 *
 * Params:
 *  c = 8進数文字
 * Return:
 *  数値
 */
private
int toOctDigit(in char c)
{
	if (('0' <= c) && (c <= '7'))
	{
		return c - '0';
	}
	return -1;
}
unittest
{
	assert(toOctDigit('0') == 0);
	assert(toOctDigit('1') == 1);
	assert(toOctDigit('2') == 2);
	assert(toOctDigit('3') == 3);
	assert(toOctDigit('4') == 4);
	assert(toOctDigit('5') == 5);
	assert(toOctDigit('6') == 6);
	assert(toOctDigit('7') == 7);
	assert(toOctDigit('8') == -1);
}


/**
 * 文字列 s の off 位置からlen長の16進数文字列を数値に変換します.
 * 変換できない文字が含まれる場合 -1 を返します.
 *
 * Params:
 *  s   = 変換する文字列
 *  off = オフセット
 *  len = 長さ
 * Return:
 *  変換後の数値
 */
private
int toHexDigit(string s, in int off, in int len)
{
	int val = 0;
	int tmpVal;
	for (int i = off; i < (off + len); i++)
	{
		tmpVal = hexToNumber(s[i]);
		if (tmpVal < 0)
		{
			return -1;
		}
		val <<= 4;
		val  |= tmpVal;
	}
	return val;
}
unittest
{
	assert(toHexDigit("1234", 3, 1) == 0x4);
	assert(toHexDigit("1234", 2, 2) == 0x34);
	assert(toHexDigit("1234", 1, 3) == 0x234);
	assert(toHexDigit("1234", 0, 4) == 0x1234);
	assert(toHexDigit("123x", 0, 4) == -1);
}


// =============================================================================
//  toEscape より使用されるメソッド
// =============================================================================
/**
 * 指定された位置 pos の文字を解析・変換し,
 * 結果を refBuff に格納します.
 *
 * pos の文字が制御文字の場合, エスケープ文字に変換します.
 *
 * Params:
 *  refBuff = 結果追加用バッファ
 *  s       = 解析文字列(全体)
 *  off     = オフセット
 * Returns:
 *  解析終了位置
 */
private
int toEscape(ref char[] refBuff, string s, in int off)
{
	if (!isCntrl(s[off]))
	{	// 変換なし
		refBuff ~= s[off];
		return off;
	}

	switch (s[off])
	{	// ESCAPE変換
		case '\n': refBuff ~= "\\n";	break;
		case '\\': refBuff ~= "\\\\";	break;
		case '\'': refBuff ~= "\\'";	break;
		case '\"': refBuff ~= "\\\"";	break;
		case '\?': refBuff ~= "\\?";	break;
		case '\r': refBuff ~= "\\r";	break;
		case '\t': refBuff ~= "\\t";	break;
		case '\v': refBuff ~= "\\v";	break;
		case '\a': refBuff ~= "\\a";	break;
		case '\b': refBuff ~= "\\b";	break;
		case '\f': refBuff ~= "\\f";	break;
		default:
			// 上記以外の制御文字は \\x にて表現
			refBuff ~= "\\x";
			refBuff ~= HEX_STRINGS[(0x0F & (s[off] >> 4))];
			refBuff ~= HEX_STRINGS[(0x0F &  s[off])];
			break;
	}
	return off;
}


// =============================================================================
//  toNative より使用されるメソッド
// =============================================================================
/**
 * 指定された位置 pos の文字を解析・変換し,
 * 結果を refBuff に格納します.
 *
 * pos の文字が '\\' の場合, 以降の文字を解析し,
 * エスケープ変換後の文字を refBuff に格納します.
 *
 * pos の文字が '\\' 以外の場合, pos の位置の文字を
 * refBuff に格納します.
 *
 * Params:
 *  refBuff = 結果追加用バッファ
 *  s       = 解析文字列(全体)
 *  off     = オフセット
 * Returns:
 *  解析終了位置
 */
private
int toNative(ref char[] refBuff, string s, in int off)
{
	int pos = off;
	if (s[pos] != '\\')
	{	// 変換なし
		refBuff ~= s[pos];
		return pos;
	}

	pos++;
	if (s.length <= pos)
	{	// 最後の文字が '\\' の場合, '\\' を追加する.
		refBuff ~= '\\';
		return pos;
	}

	switch (s[pos])
	{	// ESCAPE変換
		case 'n': refBuff ~= '\n';						break;
		case '\\':refBuff ~= '\\';						break;
		case '\'':refBuff ~= '\'';						break;
		case '"': refBuff ~= '\"';						break;
		case '?': refBuff ~= '\?';						break;
		case 'r': refBuff ~= '\r';						break;
		case 't': refBuff ~= '\t';						break;
		case 'v': refBuff ~= '\v';						break;
		case 'a': refBuff ~= '\a';						break;
		case 'b': refBuff ~= '\b';						break;
		case 'f': refBuff ~= '\f';						break;
		case 'x': pos = toNativeX    (refBuff, s, pos);	break;
		case 'u': pos = toNativeU    (refBuff, s, pos);	break;
		case 'U': pos = toNativeUU   (refBuff, s, pos);	break;
		case '&': pos = toNativeAmp  (refBuff, s, pos);	break;
		default : pos = toNativeDigit(refBuff, s, pos);	break;
	}
	return pos;
}


/**
 * [call from toNative]
 * 指定された off 位置の 'x' + 16進数 + 16進数 を
 * 文字に変換し, refBuff に追加します.
 *
 * バッファが足りない場合, マッチしない場合は,
 * "\\x" を追加します.
 *
 * Params:
 *  refBuff = 結果追加用バッファ
 *  s       = 解析文字列(全体)
 *  off     = オフセット
 * Returns:
 *  解析終了位置
 */
private
int toNativeX(ref char[] refBuff, string s, int off)
{
	if ((off + 2) >= s.length)
	{	// length が足りない -> とりあえず変換せずに追加
		refBuff ~= "\\x";
		return off;
	}

	int val = toHexDigit(s, off + 1, 2);
	if (val < 0)
	{
		refBuff ~= "\\x";
		return off;
	}
	refBuff ~= cast(char) val;
	return (off + 2);
}


/**
 * [call from toNative]
 * 指定された off 位置の 'u' + ([16進数] x 4) を
 * 文字に変換し, refBuff に追加します.
 *
 * バッファが足りない場合, マッチしない場合は,
 * "\\u" を追加します.
 *
 * Params:
 *  refBuff = 結果追加用バッファ
 *  s       = 解析文字列(全体)
 *  off     = オフセット
 * Returns:
 *  解析終了位置
 */
private
int toNativeU(ref char[] refBuff, string s, in int off)
{
	if ((off + 4) >= s.length)
	{	// length が足りない -> とりあえず変換せずに追加
		refBuff ~= "\\u";
		return off;
	}

	int val = toHexDigit(s, (off + 1), 4);
	if (val < 0)
	{	// 16進数でない文字が含まれる
		refBuff ~= "\\u";
		return off;
	}
	refBuff ~= to!(string)(cast(wchar)val);
	return (off + 4);
}


/**
 * [call from toNative]
 * 指定された off 位置の 'U' + ([16進数] x 8) を
 * 文字に変換し, refBuff に追加します.
 *
 * バッファが足りない場合, マッチしない場合は,
 * "\\u" を追加します.
 *
 * Params:
 *  refBuff = 結果追加用バッファ
 *  s       = 解析文字列(全体)
 *  off     = オフセット
 * Returns:
 *  解析終了位置
 */
private
int toNativeUU(ref char[] refBuff, string s, in int off)
{
	if ((off + 8) >= s.length)
	{	// length が足りない -> とりあえず変換せずに追加
		refBuff ~= "\\U";
		return off;
	}

	int val = toHexDigit(s, (off + 1), 8);
	if (val < 0)
	{	// 16進数でない文字が含まれる
		refBuff ~= "\\U";
		return off;
	}
	refBuff ~= to!(string)(cast(dchar)val);
	return (off + 8);
}


/**
 * [call from toNative]
 * 指定された off 位置の '&' +  文字実態名 を
 * 文字に変換し, refBuff に追加します.
 *
 * バッファが足りない場合, マッチしない場合は,
 * "\\&" を追加します.
 *
 * Params:
 *  refBuff = 結果追加用バッファ
 *  s       = 解析文字列(全体)
 *  off     = オフセット
 * Returns:
 *  解析終了位置
 */
private
int toNativeAmp(ref char[] refBuff, string s, in int off)
{
	int pos = indexOf(s[off .. $], ';');
	if (pos < 0)
	{	// 実態文字名ではない
		refBuff ~= "\\&";
		return off;
	}

	string tmp = s[(off + 1) .. (off + pos)];
	if (!(tmp in AMP_STRINGS))
	{	// 未サポートの実態名
		refBuff ~= "\\&";
		return off;
	}

	refBuff ~= to!(string)(AMP_STRINGS[tmp]);
	return (off + pos);
}


/**
 * [call from toNative]
 * 指定された off 位置の 8 進数 (最大3桁) を
 * 文字に変換し, refBuff に追加します.
 *
 * off 位置の文字が 8 進数出ない場合, '\\' + [off位置の文字] を
 * refBuff に追加します.
 *
 * Params:
 *  refBuff = 結果追加用バッファ
 *  s       = 解析文字列(全体)
 *  off     = オフセット
 * Returns:
 *  解析終了位置
 */
private
int toNativeDigit(ref char[] refBuff, string s, in int off)
{
	int pos = off;
	int val = toOctDigit(s[pos]);
	if (val < 0)
	{	// 1つめが8進数数値でなければそのまま文字列を格納
		refBuff ~= '\\';
		refBuff ~= s[pos];
		return pos;
	}

	// 解析終了位置設定 [8進数は最大3桁]
	int end = pos + 3;
	if (end > s.length)
	{
		end = s.length;
	}

	// 次の文字解析 [はじめに対象かどうかを判定]
	while ((pos + 1) < end)
	{
		int tmpVal = toOctDigit(s[pos + 1]);
		if (tmpVal < 0)
		{
			break;
		}
		val    <<= 3;
		val     |= tmpVal;
		pos++;
	}

	refBuff ~= cast(char) val;
	return pos;
}



/** 文字実態名マップ. */
dchar[char[]] AMP_STRINGS;

/** 16進数文字. */
string HEX_STRINGS = "0123456789ABCDEF";

/** 制御文字列として扱う文字. */
string CNTRL_CHARS = "\\\'\"\?";

private
static this()
{
	AMP_STRINGS["quot"]     = '\&quot;';
	AMP_STRINGS["amp"]      = '\&amp;';
	AMP_STRINGS["lt"]       = '\&lt;';
	AMP_STRINGS["gt"]       = '\&gt;';
	AMP_STRINGS["OElig"]    = '\&OElig;';
	AMP_STRINGS["oelig"]    = '\&oelig;';
	AMP_STRINGS["Scaron"]   = '\&Scaron;';
	AMP_STRINGS["scaron"]   = '\&scaron;';
	AMP_STRINGS["Yuml"]     = '\&Yuml;';
	AMP_STRINGS["circ"]     = '\&circ;';
	AMP_STRINGS["tilde"]    = '\&tilde;';
	AMP_STRINGS["ensp"]     = '\&ensp;';
	AMP_STRINGS["emsp"]     = '\&emsp;';
	AMP_STRINGS["thinsp"]   = '\&thinsp;';
	AMP_STRINGS["zwnj"]     = '\&zwnj;';
	AMP_STRINGS["zwj"]      = '\&zwj;';
	AMP_STRINGS["lrm"]      = '\&lrm;';
	AMP_STRINGS["rlm"]      = '\&rlm;';
	AMP_STRINGS["ndash"]    = '\&ndash;';
	AMP_STRINGS["mdash"]    = '\&mdash;';
	AMP_STRINGS["lsquo"]    = '\&lsquo;';
	AMP_STRINGS["rsquo"]    = '\&rsquo;';
	AMP_STRINGS["sbquo"]    = '\&sbquo;';
	AMP_STRINGS["ldquo"]    = '\&ldquo;';
	AMP_STRINGS["rdquo"]    = '\&rdquo;';
	AMP_STRINGS["bdquo"]    = '\&bdquo;';
	AMP_STRINGS["dagger"]   = '\&dagger;';
	AMP_STRINGS["Dagger"]   = '\&Dagger;';
	AMP_STRINGS["permil"]   = '\&permil;';
	AMP_STRINGS["lsaquo"]   = '\&lsaquo;';
	AMP_STRINGS["rsaquo"]   = '\&rsaquo;';
	AMP_STRINGS["euro"]     = '\&euro;';
	AMP_STRINGS["nbsp"]     = '\&nbsp;';
	AMP_STRINGS["iexcl"]    = '\&iexcl;';
	AMP_STRINGS["cent"]     = '\&cent;';
	AMP_STRINGS["pound"]    = '\&pound;';
	AMP_STRINGS["curren"]   = '\&curren;';
	AMP_STRINGS["yen"]      = '\&yen;';
	AMP_STRINGS["brvbar"]   = '\&brvbar;';
	AMP_STRINGS["sect"]     = '\&sect;';
	AMP_STRINGS["uml"]      = '\&uml;';
	AMP_STRINGS["copy"]     = '\&copy;';
	AMP_STRINGS["ordf"]     = '\&ordf;';
	AMP_STRINGS["laquo"]    = '\&laquo;';
	AMP_STRINGS["not"]      = '\&not;';
	AMP_STRINGS["shy"]      = '\&shy;';
	AMP_STRINGS["reg"]      = '\&reg;';
	AMP_STRINGS["macr"]     = '\&macr;';
	AMP_STRINGS["deg"]      = '\&deg;';
	AMP_STRINGS["plusmn"]   = '\&plusmn;';
	AMP_STRINGS["sup2"]     = '\&sup2;';
	AMP_STRINGS["sup3"]     = '\&sup3;';
	AMP_STRINGS["acute"]    = '\&acute;';
	AMP_STRINGS["micro"]    = '\&micro;';
	AMP_STRINGS["para"]     = '\&para;';
	AMP_STRINGS["middot"]   = '\&middot;';
	AMP_STRINGS["cedil"]    = '\&cedil;';
	AMP_STRINGS["sup1"]     = '\&sup1;';
	AMP_STRINGS["ordm"]     = '\&ordm;';
	AMP_STRINGS["raquo"]    = '\&raquo;';
	AMP_STRINGS["frac14"]   = '\&frac14;';
	AMP_STRINGS["frac12"]   = '\&frac12;';
	AMP_STRINGS["frac34"]   = '\&frac34;';
	AMP_STRINGS["iquest"]   = '\&iquest;';
	AMP_STRINGS["Agrave"]   = '\&Agrave;';
	AMP_STRINGS["Aacute"]   = '\&Aacute;';
	AMP_STRINGS["Acirc"]    = '\&Acirc;';
	AMP_STRINGS["Atilde"]   = '\&Atilde;';
	AMP_STRINGS["Auml"]     = '\&Auml;';
	AMP_STRINGS["Aring"]    = '\&Aring;';
	AMP_STRINGS["AElig"]    = '\&AElig;';
	AMP_STRINGS["Ccedil"]   = '\&Ccedil;';
	AMP_STRINGS["Egrave"]   = '\&Egrave;';
	AMP_STRINGS["Eacute"]   = '\&Eacute;';
	AMP_STRINGS["Ecirc"]    = '\&Ecirc;';
	AMP_STRINGS["Euml"]     = '\&Euml;';
	AMP_STRINGS["Igrave"]   = '\&Igrave;';
	AMP_STRINGS["Iacute"]   = '\&Iacute;';
	AMP_STRINGS["Icirc"]    = '\&Icirc;';
	AMP_STRINGS["Iuml"]     = '\&Iuml;';
	AMP_STRINGS["ETH"]      = '\&ETH;';
	AMP_STRINGS["Ntilde"]   = '\&Ntilde;';
	AMP_STRINGS["Ograve"]   = '\&Ograve;';
	AMP_STRINGS["Oacute"]   = '\&Oacute;';
	AMP_STRINGS["Ocirc"]    = '\&Ocirc;';
	AMP_STRINGS["Otilde"]   = '\&Otilde;';
	AMP_STRINGS["Ouml"]     = '\&Ouml;';
	AMP_STRINGS["times"]    = '\&times;';
	AMP_STRINGS["Oslash"]   = '\&Oslash;';
	AMP_STRINGS["Ugrave"]   = '\&Ugrave;';
	AMP_STRINGS["Uacute"]   = '\&Uacute;';
	AMP_STRINGS["Ucirc"]    = '\&Ucirc;';
	AMP_STRINGS["Uuml"]     = '\&Uuml;';
	AMP_STRINGS["Yacute"]   = '\&Yacute;';
	AMP_STRINGS["THORN"]    = '\&THORN;';
	AMP_STRINGS["szlig"]    = '\&szlig;';
	AMP_STRINGS["agrave"]   = '\&agrave;';
	AMP_STRINGS["aacute"]   = '\&aacute;';
	AMP_STRINGS["acirc"]    = '\&acirc;';
	AMP_STRINGS["atilde"]   = '\&atilde;';
	AMP_STRINGS["auml"]     = '\&auml;';
	AMP_STRINGS["aring"]    = '\&aring;';
	AMP_STRINGS["aelig"]    = '\&aelig;';
	AMP_STRINGS["ccedil"]   = '\&ccedil;';
	AMP_STRINGS["egrave"]   = '\&egrave;';
	AMP_STRINGS["eacute"]   = '\&eacute;';
	AMP_STRINGS["ecirc"]    = '\&ecirc;';
	AMP_STRINGS["euml"]     = '\&euml;';
	AMP_STRINGS["igrave"]   = '\&igrave;';
	AMP_STRINGS["iacute"]   = '\&iacute;';
	AMP_STRINGS["icirc"]    = '\&icirc;';
	AMP_STRINGS["iuml"]     = '\&iuml;';
	AMP_STRINGS["eth"]      = '\&eth;';
	AMP_STRINGS["ntilde"]   = '\&ntilde;';
	AMP_STRINGS["ograve"]   = '\&ograve;';
	AMP_STRINGS["oacute"]   = '\&oacute;';
	AMP_STRINGS["ocirc"]    = '\&ocirc;';
	AMP_STRINGS["otilde"]   = '\&otilde;';
	AMP_STRINGS["ouml"]     = '\&ouml;';
	AMP_STRINGS["divide"]   = '\&divide;';
	AMP_STRINGS["oslash"]   = '\&oslash;';
	AMP_STRINGS["ugrave"]   = '\&ugrave;';
	AMP_STRINGS["uacute"]   = '\&uacute;';
	AMP_STRINGS["ucirc"]    = '\&ucirc;';
	AMP_STRINGS["uuml"]     = '\&uuml;';
	AMP_STRINGS["yacute"]   = '\&yacute;';
	AMP_STRINGS["thorn"]    = '\&thorn;';
	AMP_STRINGS["yuml"]     = '\&yuml;';
	AMP_STRINGS["fnof"]     = '\&fnof;';
	AMP_STRINGS["Alpha"]    = '\&Alpha;';
	AMP_STRINGS["Beta"]     = '\&Beta;';
	AMP_STRINGS["Gamma"]    = '\&Gamma;';
	AMP_STRINGS["Delta"]    = '\&Delta;';
	AMP_STRINGS["Epsilon"]  = '\&Epsilon;';
	AMP_STRINGS["Zeta"]     = '\&Zeta;';
	AMP_STRINGS["Eta"]      = '\&Eta;';
	AMP_STRINGS["Theta"]    = '\&Theta;';
	AMP_STRINGS["Iota"]     = '\&Iota;';
	AMP_STRINGS["Kappa"]    = '\&Kappa;';
	AMP_STRINGS["Lambda"]   = '\&Lambda;';
	AMP_STRINGS["Mu"]       = '\&Mu;';
	AMP_STRINGS["Nu"]       = '\&Nu;';
	AMP_STRINGS["Xi"]       = '\&Xi;';
	AMP_STRINGS["Omicron"]  = '\&Omicron;';
	AMP_STRINGS["Pi"]       = '\&Pi;';
	AMP_STRINGS["Rho"]      = '\&Rho;';
	AMP_STRINGS["Sigma"]    = '\&Sigma;';
	AMP_STRINGS["Tau"]      = '\&Tau;';
	AMP_STRINGS["Upsilon"]  = '\&Upsilon;';
	AMP_STRINGS["Phi"]      = '\&Phi;';
	AMP_STRINGS["Chi"]      = '\&Chi;';
	AMP_STRINGS["Psi"]      = '\&Psi;';
	AMP_STRINGS["Omega"]    = '\&Omega;';
	AMP_STRINGS["alpha"]    = '\&alpha;';
	AMP_STRINGS["beta"]     = '\&beta;';
	AMP_STRINGS["gamma"]    = '\&gamma;';
	AMP_STRINGS["delta"]    = '\&delta;';
	AMP_STRINGS["epsilon"]  = '\&epsilon;';
	AMP_STRINGS["zeta"]     = '\&zeta;';
	AMP_STRINGS["eta"]      = '\&eta;';
	AMP_STRINGS["theta"]    = '\&theta;';
	AMP_STRINGS["iota"]     = '\&iota;';
	AMP_STRINGS["kappa"]    = '\&kappa;';
	AMP_STRINGS["lambda"]   = '\&lambda;';
	AMP_STRINGS["mu"]       = '\&mu;';
	AMP_STRINGS["nu"]       = '\&nu;';
	AMP_STRINGS["xi"]       = '\&xi;';
	AMP_STRINGS["omicron"]  = '\&omicron;';
	AMP_STRINGS["pi"]       = '\&pi;';
	AMP_STRINGS["rho"]      = '\&rho;';
	AMP_STRINGS["sigmaf"]   = '\&sigmaf;';
	AMP_STRINGS["sigma"]    = '\&sigma;';
	AMP_STRINGS["tau"]      = '\&tau;';
	AMP_STRINGS["upsilon"]  = '\&upsilon;';
	AMP_STRINGS["phi"]      = '\&phi;';
	AMP_STRINGS["chi"]      = '\&chi;';
	AMP_STRINGS["psi"]      = '\&psi;';
	AMP_STRINGS["omega"]    = '\&omega;';
	AMP_STRINGS["thetasym"] = '\&thetasym;';
	AMP_STRINGS["upsih"]    = '\&upsih;';
	AMP_STRINGS["piv"]      = '\&piv;';
	AMP_STRINGS["bull"]     = '\&bull;';
	AMP_STRINGS["hellip"]   = '\&hellip;';
	AMP_STRINGS["prime"]    = '\&prime;';
	AMP_STRINGS["Prime"]    = '\&Prime;';
	AMP_STRINGS["oline"]    = '\&oline;';
	AMP_STRINGS["frasl"]    = '\&frasl;';
	AMP_STRINGS["weierp"]   = '\&weierp;';
	AMP_STRINGS["image"]    = '\&image;';
	AMP_STRINGS["real"]     = '\&real;';
	AMP_STRINGS["trade"]    = '\&trade;';
	AMP_STRINGS["alefsym"]  = '\&alefsym;';
	AMP_STRINGS["larr"]     = '\&larr;';
	AMP_STRINGS["uarr"]     = '\&uarr;';
	AMP_STRINGS["rarr"]     = '\&rarr;';
	AMP_STRINGS["darr"]     = '\&darr;';
	AMP_STRINGS["harr"]     = '\&harr;';
	AMP_STRINGS["crarr"]    = '\&crarr;';
	AMP_STRINGS["lArr"]     = '\&lArr;';
	AMP_STRINGS["uArr"]     = '\&uArr;';
	AMP_STRINGS["rArr"]     = '\&rArr;';
	AMP_STRINGS["dArr"]     = '\&dArr;';
	AMP_STRINGS["hArr"]     = '\&hArr;';
	AMP_STRINGS["forall"]   = '\&forall;';
	AMP_STRINGS["part"]     = '\&part;';
	AMP_STRINGS["exist"]    = '\&exist;';
	AMP_STRINGS["empty"]    = '\&empty;';
	AMP_STRINGS["nabla"]    = '\&nabla;';
	AMP_STRINGS["isin"]     = '\&isin;';
	AMP_STRINGS["notin"]    = '\&notin;';
	AMP_STRINGS["ni"]       = '\&ni;';
	AMP_STRINGS["prod"]     = '\&prod;';
	AMP_STRINGS["sum"]      = '\&sum;';
	AMP_STRINGS["minus"]    = '\&minus;';
	AMP_STRINGS["lowast"]   = '\&lowast;';
	AMP_STRINGS["radic"]    = '\&radic;';
	AMP_STRINGS["prop"]     = '\&prop;';
	AMP_STRINGS["infin"]    = '\&infin;';
	AMP_STRINGS["ang"]      = '\&ang;';
	AMP_STRINGS["and"]      = '\&and;';
	AMP_STRINGS["or"]       = '\&or;';
	AMP_STRINGS["cap"]      = '\&cap;';
	AMP_STRINGS["cup"]      = '\&cup;';
	AMP_STRINGS["int"]      = '\&int;';
	AMP_STRINGS["there4"]   = '\&there4;';
	AMP_STRINGS["sim"]      = '\&sim;';
	AMP_STRINGS["cong"]     = '\&cong;';
	AMP_STRINGS["asymp"]    = '\&asymp;';
	AMP_STRINGS["ne"]       = '\&ne;';
	AMP_STRINGS["equiv"]    = '\&equiv;';
	AMP_STRINGS["le"]       = '\&le;';
	AMP_STRINGS["ge"]       = '\&ge;';
	AMP_STRINGS["sub"]      = '\&sub;';
	AMP_STRINGS["sup"]      = '\&sup;';
	AMP_STRINGS["nsub"]     = '\&nsub;';
	AMP_STRINGS["sube"]     = '\&sube;';
	AMP_STRINGS["supe"]     = '\&supe;';
	AMP_STRINGS["oplus"]    = '\&oplus;';
	AMP_STRINGS["otimes"]   = '\&otimes;';
	AMP_STRINGS["perp"]     = '\&perp;';
	AMP_STRINGS["sdot"]     = '\&sdot;';
	AMP_STRINGS["lceil"]    = '\&lceil;';
	AMP_STRINGS["rceil"]    = '\&rceil;';
	AMP_STRINGS["lfloor"]   = '\&lfloor;';
	AMP_STRINGS["rfloor"]   = '\&rfloor;';
	AMP_STRINGS["lang"]     = '\&lang;';
	AMP_STRINGS["rang"]     = '\&rang;';
	AMP_STRINGS["loz"]      = '\&loz;';
	AMP_STRINGS["spades"]   = '\&spades;';
	AMP_STRINGS["clubs"]    = '\&clubs;';
	AMP_STRINGS["hearts"]   = '\&hearts;';
	AMP_STRINGS["diams"]    = '\&diams;';
}

