
/**
 * @name	CeL function for math application.
 * @fileoverview
 * 本檔案包含了生成 math application 的 functions。
 * @since	2014/10/3
 * @example
 * <code>
 * CeL.run('application.math', function() {
 * 	// ..
 * });
 * </code>
 */

'use strict';
if (typeof CeL === 'function')
CeL.run(
{
name : 'application.math',
// includes() @ data.code.compatibility.
require : 'data.code.compatibility.|data.math.',
code : function module_code(library_namespace) {


/**
 * null module constructor
 * @class	出數學題目用的 functions
 */
var _// JSDT:_module_
= function() {
	//	null module constructor
};

/**
 * for JSDT: 有 prototype 才會將之當作 Class
 */
_// JSDT:_module_
.prototype = {
};


//----------------------------------------------------- //

(function() {
	runCode.setR = 0;
	for (var i = 0, j, t, s, n_e; i < 10;) {
		t = 2000 + 8000 * Math.random();
		s = get_random_prime.get_different_number_set(3, t, t / 8);
		if (s.LCM > 9999)
			continue;
		n_e = [];
		n_e[s.GCD] = 1;
		for (j = 0; j < s.length; j++)
			if (n_e[s[j]])
				continue;
			else
				n_e[s[j]] = 1;
		sl([ s.GCD, s.LCM ] + '<b style="color:#c4a">;</b> ' + s);
		i++;
	}
});


//----------------------------------------------------- //

// 中式短除法(Chinese short division)並非 short division.
// https://en.wikipedia.org/wiki/Short_division
function draw_short_division(naturals, layer, GCD_only) {
	if (!Array.isArray(naturals))
		if (isNaN(layer))
			naturals = [ naturals ];
		else
			naturals = arguments, layer = null;

	var i, length = naturals.length | 0, divisor, cell_width_em = 2,
	//
	natural_Array = [];
	for (i = 0; i < length; i++) {
		divisor = +naturals[i];
		if (0 < divisor && divisor < Number.MAX_SAFE_INTEGER) {
			natural_Array.push(divisor);
			if (cell_width_em < String(divisor).length * .6)
				cell_width_em = Math.ceil(String(divisor).length * .6);
		}
	}
	length = (naturals = Array.prototype.slice.call(natural_Array)).length | 0;

	var block = [], count = 0,
		//
		_GCD = library_namespace.GCD(natural_Array),
		//
		GCD = library_namespace.factorize(_GCD);

	library_namespace.debug(length + ' Naturals: '+natural_Array+'.',2);
	if (GCD) {
		// assert: _GCD > 1
		if (_GCD !== (divisor = GCD.toString(true)))
			_GCD += ' = ' + divisor;

		// phase 1: 處理 GCD 部分。
		for (divisor in GCD) {
			divisor |= 0;
			for (var j = 0; j < GCD[divisor] | 0; j++)
				if (length !== 1 || natural_Array[0] !== divisor) {
					block.push(draw_short_division.add_line(natural_Array, cell_width_em, divisor, count++, true));
					for (i = 0; i < length; i++)
						natural_Array[i] = natural_Array[i] / divisor;
				}
		}
	}

	// phase 2: 處理 LCM 部分。
	// TODO: 按大小排列。
	GCD = 0;
	do {
		var LCM = natural_Array[0];
		for (i = 1; i < length; i++)
			if (natural_Array[i] > 1 && (GCD = library_namespace.GCD(LCM, natural_Array[i])) > 1) {
				divisor = +library_namespace.first_factor(GCD);
				block.push(draw_short_division.add_line(natural_Array, cell_width_em, divisor, count++));
				for (i = 0; i < length; i++)
					if (natural_Array[i] % divisor === 0)
						natural_Array[i] = natural_Array[i] / divisor;
				break;
			}
	} while (GCD > 1);

	// 依照各種不同類之輸入，顯示不同備註標示。
	if (length === 1) {
		// 質因數分解。
		block.push(
			draw_short_division.add_line(natural_Array, cell_width_em),
			{
				div: [
					_GCD
				]
			}
		);

	} else {
		i = library_namespace.LCM(naturals);
		block.push(
			draw_short_division.add_line(natural_Array, cell_width_em),
			{
				div: [
					'GCD',
					// '(', naturals.join(', '), ')',
					' = ',
					_GCD
				],
				S: draw_short_division.GCD_style
			}, {
				div: [
					'LCM',
					// '(', naturals.join(', '), ')',
					' = ',
					i,
					' = ',
					library_namespace.factorize(i).toString(true)
				]
			}
		);
	}

	// 最後收尾。
	block = {
		div: block,
		style: 'width:' + (1 + (length + (1 < length ? 2 : 1)) * cell_width_em | 0) + 'em;background-color:#def',
		C: 'short_division'
	};
	return layer ? new_node(block, layer) : block;
}
draw_short_division.GCD_style = 'color:#f79;';

draw_short_division.add_line = function(naturals, cell_width_em, divisor, count, phase_GCD) {
	library_namespace.debug(divisor + ': ' + naturals, 3, 'draw_short_division.add_line');
	var line = [];
	naturals.forEach(function(natural, index) {
		line.push({
			span: natural,
			S: 'display:inline-block;text-align:right;padding-right:.2em;width:'
				+ (cell_width_em + (index ? 0 : .5 - count / 5)).to_fixed(2) + 'em'
		}, ' ');
	});
	line.pop();

	if (divisor)
		line = [{
			span: divisor,
			S: 'text-align:right;padding-right:.2em;'
		}, {
			span: line,
			S: 'border-left:1pt solid #88f;border-bottom:1pt solid #88f'
		}];

	return {
		div: line,
		S: 'clear:both;text-align:right;' + (phase_GCD ? draw_short_division.GCD_style : '')
	};
};


_.draw_short_division = draw_short_division;

/*

CeL.run('application.math', function() {
	CeL.draw_short_division([12], [ document.body, 2 ]);
	CeL.draw_short_division([12, 18], [ document.body, 2 ]);
	CeL.draw_short_division([12, 18, 24], [ document.body, 2 ]);
});

*/


// ---------------------------------------------------------------------------------------------- //

var new_node = function () {
	var func = library_namespace.DOM.new_node;
	if (func)
		return (new_node = func).apply(null, arguments);
},
//
check_MathML = function() {
	var math_node = new_node({
		div : {
			span : 'normal'
		},
		S : 'line-height:1em;visibility:hidden'
	}, document.body);

	if (!math_node)
		return;

	new_node({
		math : {
			mfrac : [ {
				mi : 'test'
			}, {
				mi : 'MathML'
			} ]
		}
	}, math_node, 'mathml');

	// Firefox/37.0 不需要 setTimeout()。
	// setTimeout(check_MathML.check.bind(math_node), 0);
	// return check_MathML.check.call(math_node, true);

	return check_MathML.check.call(math_node);
};

// 2015/1/1: Firefox only. 僅 firefox 回傳 true。
check_MathML.check = function(no_remove) {
	// library_namespace.debug(this);

	Object.defineProperty(_, 'support_MathML', {
		// method 1:
		// 分數 2/3 之 2 的 offsetTop 應該比 3 更高一點。
		// 但在 ff 中，沒有 .offsetTop。
		// var mfrac = this.lastChild.firstChild;
		// value : mfrac.firstChild.offsetTop < mfrac.lastChild.offsetTop;

		// method 2: 因為插入 <mfrac>，<div> 應該比一般單行文字更高一點。但在 Chrome，兩者本身即有差別。
		value : this.offsetHeight > this.firstChild.offsetHeight + 3
	});

	if (!_.support_MathML)
		library_namespace.debug('The browser does not support MathML. 您的瀏覽器不支援 MathML，或是 MathML 功能已被關閉。');
	if (no_remove !== true) {
		// library_namespace.remove_node(this);
		document.body.removeChild(this);
	}

	return _.support_MathML;
};


// assert: support MathML 也必定 support Object.defineProperty().
Object.defineProperty(_, 'support_MathML', {
	configurable : true,
	get : check_MathML
});


// ----------------------------------------------------- //

/**
 * 以 MathML 表現數學運算式。<br />
 * 將 HTML 中 &lt;math>expression&lt;/math> 皆轉為 MathML。<br />
 * parse math expression & output MathML.<br />
 * 
 * TODO: calculator, vector, ℕℤℚℝℂ, ∈∉
 * 
 * @example <code>

// <script>
CeL.run([
	// for new_node()
	'interact.DOM', 'application.math' ], function() {
	CeL.application.math.convert_MathML();
});

// <html>
<math>α=r×β</math><hr />
<math>5%*6=30%</math><hr />
<math>3*5/7</math><hr />
<math>3*(5/7)</math><hr />
<math>(4+5)/2</math><hr />
<math>5^4*3+2-4^2/5</math><hr />
<math>(a/b)/(c/d)</math><hr />
<math>資本收入=資本收益率×資本</math><hr />
<math>資本收入/國民年收入=資本收益率×(資本存量/國民年收入)</math><hr />
<math>
1/2+(1+2)/(2+3%)+((2+3))/((3+4))
</math><hr />
<math>
43+(54+5*(3+4)/3)*2
</math><hr />
<math>x_2^4</math><hr />
<math>x^2+y_1+z_12^34</math><hr />
<math>
x_2^4 = 3/2,   y = 4/3,   z = 5/4
</math><hr />
<math>
x_2^4 = 3/2
y = 4/3
z = 5/4
</math><hr />
<math>4^(1/7)</math><hr />

<math>√453</math><hr />
<math>√4e</math><hr />
<math>√-4e</math><hr />
<math>√(4e+3)</math><hr />
<math>∛45</math><hr />
<math>x=1/√3</math><hr />

<math>3&lt;4</math><hr />
<math>log_4(45+2)</math><hr />
<math>log_4 452</math><hr />
<math>log_e 452</math><hr />
<math>sin(π/2) = sin(pi/2) = sin(90°)</math><hr />
<math>sin π/2 = (sin π)/2</math><hr />
<math>sin(34π) = sin 34π</math><hr />
<math>sin^-1(2π) = sin^-1 2π = sin^(-1)(2 pi)</math><hr />
<math>log_4^2 452</math><hr />
<math>log_4^2(34+2)</math><hr />
<math>2e+3^2π+4π</math><hr />
<math>-4e</math><hr />
<math>(5+3)⁄(5-4)</math><hr />
<math>7^(1/4) , 4√7</math><hr />
<math>x_y^2 x^2+2^(1/3)^8+4^(1/7)^6</math><hr />
<math>1+(b^2-4a c)^(1/5)</math><hr />
<math>x=(-b±√(b^2-4a c))/2a</math><hr />
<math>y=x(x^2-1)</math><hr />

<math>(^2)Fe</math><hr />
<math>(^12)C</math><hr />
<math>(_6^12)C</math><hr />
<math>(_6)C</math><hr />
<math>(_2^1)x_4^3</math><hr />
<math>(_2^1)x_4</math><hr />


// TODO:
<math>{ a; b; c, d, e }</math><hr />
<math>{{4, 5, 6}, {7, 8, 9}, {1, 2, 3}}×((3,4,5),(5,6,7),(7,8,9))</math><hr />
<math>{{1,2},{3,4}}((5),(6))</math><hr />

<math>x_y^2 x^2 2^(1/3)^8 4^(1/7)^6</math><hr />
<math>(_(a+b))x</math><hr />

munderover
<math>∫_2^4 dy/dx</math><hr />


// reference
http://www.w3.org/TR/MathML/chapter3.html#id.3.1.3.2
https://developer.mozilla.org/en-US/docs/Web/MathML/Element
http://www.wolframalpha.com/examples/Math.html
http://reference.wolfram.com/language/ref/format/MathML.html

 * </code>
 * 
 * @see
 */
function convert_MathML(handler) {
	if (!library_namespace.remove_all_child)
		return;
	// assert: library_namespace.DOM is loaded.
	if (!_.support_MathML) {
		library_namespace.warn('The browser does not support MathML!');
		if (!library_namespace.is_WWW(true))
			return;
	}

	// MathML nodes
	var nodes = document.getElementsByTagName('math'), length = nodes.length, i = 0, node;

	if (!handler)
		handler = convert_MathML.handler['default'];
	else if (handler in convert_MathML.handler)
		handler = convert_MathML.handler[handler];

	for (; i < length; i++) {
		node = nodes[i];
		if (!_.support_MathML) {
			if (!node.title && node.getAttribute(convert_MathML.default_attribute)) {
				// useless... Chrome 不會像 <span> 一般顯示 .title。
				node.setAttribute('title', node.getAttribute(convert_MathML.default_attribute));
			}
			continue;
		}

		if (false && typeof node.getAttribute !== 'function')
			return;

		var text;
		if (node.childNodes.length !== 1
				|| node.firstChild.nodeType !== document.TEXT_NODE
				//
				|| !(text = node.firstChild.nodeValue.trim()))
			continue;
		// temporary usage.
		var attribute = convert_MathML.default_attribute,
		//
		structure = node.getAttribute(attribute) || node.getAttribute(attribute = 'title');
		if (structure) {
			// 當可以使用 <math> 時，把原先展示的內容物 .childNodes 轉而擺到 .title 去。
			// 使用例： <math alt="e^(-)" title="e-">電子</math>, <math alt="H_2 O" title="水 H2O">水分子</math>

			// 避免覆蓋原先的 .title。.title 通常擺更詳細的資訊。
			if (!node.getAttribute(attribute = 'title')) {
				node.setAttribute('title', text);
			}
			text = '\n' + structure;
		}
		if (!node.getAttribute('xmlns')) {
			node.setAttribute('xmlns', "http://www.w3.org/1998/Math/MathML");
		}
		// library_namespace.debug(node);

		structure = convert_MathML.parse(text);

		if (!structure[0]) {
			// 若是所有 children 都是等式，則將之括弧起來。
			var equalities = [ '{' ], j = 1, tmp;
			for (; j < structure.length; j++) {
				if (tmp = structure[j])
					if (Array.isArray(tmp) && convert_MathML.RELATIONSHIP_PATTERN.test(tmp[0])) {
						equalities.push(tmp);
					} else if (typeof tmp !== 'string' || tmp.trim()) {
						equalities = null;
						break;
					}
			}
			if (equalities)
				structure = equalities;
		}

		structure = convert_MathML.reduce(structure, node, handler);
		// library_namespace.debug('convert_MathML: structure:');
		// library_namespace.debug(structure);
		library_namespace.remove_all_child(node);
		new_node(structure, node, 'mathml');
	}
}

_.convert_MathML = convert_MathML;

convert_MathML.default_attribute = 'alt';

convert_MathML.reduce_quote = function(operand) {
	var row = operand.mrow;
	if (!row || !Array.isArray(row) || row.length !== 3) {
		return operand;
	}
	var operator = row[0].mo + row[2].mo;
	if (operator === '{}' || operator === '()') {
		// assert: Array.isArray(row[1].mrow)
		return row[1];
	}
	return operand;
};

convert_MathML.handler = {
	// toString()
	// 運算元,運算子,運算元(操作符/算符/算子)
	string : function(operand_1, operator, operand_2) {
		if (!operator && Array.isArray(operand_1))
			return operand_1.join(' ');
		switch (operator) {
		case '()':
			return '(' + operand_1 + ')';

		case 'm':
			// 以表格呈現。
			operand_1.forEach(function(row, index) {
				operand_1[index] = '{' + row.join(',') + '}';
			});
		case '{}':
			return '{' + operand_1 + '}';

		case ',':
			return operand_1.join(',');
		case '{':
			return operand_1.join('\n');
		}
		return operand_1 + (operator || '') + (operand_2 || '');
	},

	mathml : function(operand_1, operator, operand_2) {
		if (!operator && Array.isArray(operand_1))
			return operand_1;

		switch (operator) {
		case '{}':
		case '()':
			if (typeof operand_1 !== 'object') {
				operand_1 = convert_MathML.parse_scalar(operand_1);

			} else if (!operand_1.mfrac && Array.isArray(operand_1)) {
				operand_1 = [ {
					mo : operator === '{}' ? '{' : '('
				}, {
					mrow : operand_1
				}, {
					mo : operator === '{}' ? '}' : ')'
				} ];
			} else if (false && !operand_1.mfrac) {
				// for !operand_1.mfrac: "(1/2)" → "1/2"
				operand_1 = {
					// deprecated MathML <mfenced> element
					mfenced : operand_1,
					separators : ''
				};
				if (operator === '{}') {
					operand_1.open = '{';
					operand_1.close = '}';
				}
			}
			return operand_1;

		case 'm':
			// 以表格呈現矩陣。
			operand_1.forEach(function(row, index) {
				row.forEach(function(expression, index) {
					row[index] = {
						mtd : expression
					};
				});
				operand_1[index] = {
					mtr : row
				};
			});
			operand_1 = {
				mfenced : {
					mtable : operand_1
				}
			};
			if (operand_2 !== '()')
				operand_1.open = '[', operand_1.close = ']';
			return operand_1;

		case '{':
			// 以表格呈現方程式組。
			operand_1.forEach(function(equality, index) {
				operand_1[index] = {
					mtr : {
						mtd : equality
					}
				};
			});
			return {
				mfenced : {
					mtable : operand_1
				},
				open : '{',
				close : ''
			};

		case ',':
		case ';':
			operand_2 = [];
			operand_1.forEach(function(expression) {
				operand_2.push(convert_MathML.parse_scalar(expression), {
					mo : operator
				});
			});
			operand_2.pop();
			return operand_2;

		case '/':
		case '⁄':
			// ↑ Fraction slash
		case '∕':
			// ↑ Division slash
			operand_1 = convert_MathML.parse_scalar(operand_1);
			operand_2 = convert_MathML.parse_scalar(operand_2);
			// "(1)/(2)" → "1/(2)"
			operand_1 = convert_MathML.reduce_quote(operand_1);
			// "(1)/(2)" → "(1)/2"
			operand_2 = convert_MathML.reduce_quote(operand_2);
			if (false) {
				if (operand_1.mfenced)
					// "(1)/(2)" → "1/2"
					operand_1 = convert_MathML.parse_scalar(operand_1.mfenced);
				if (operand_2.mfenced)
					// "(1)/(2)" → "1/2"
					operand_2 = convert_MathML.parse_scalar(operand_2.mfenced);
			}
			// <mfrac> <mi>numerator</mi> <mi>denominator</mi> </mfrac>
			operand_1 = {
				mfrac : [ operand_1, operand_2 ]
			};
			// 除了這些外，皆當作分數，上下表示。
			if (operator === '⁄' || operator === '∕')
				operand_1.bevelled = true;
			return operand_1;

		case '^':
			operand_1 = convert_MathML.parse_scalar(operand_1);
			operand_2 = convert_MathML.parse_scalar(operand_2);
			if (operand_2.mfenced)
				// 去除括號 "()"。
				// "7^(2/3)" → "<msup>7 2/3</msup>"
				operand_2 = convert_MathML.parse_scalar(operand_2.mfenced);
			if (Array.isArray(operand_2.mfrac) && operand_2.mfrac[0].mn === 1) {
				// 去除 operand_1 之括號 "()"。
				if (operand_1.mfenced)
					operand_1 = convert_MathML.parse_scalar(operand_1.mfenced);
				// (operand_1) 的 (operand_2.mfrac[1]) 次方根。
				// "7^(1/3)" → "<mroot> 7 3 </mroot>"
				return {
					mroot : operand_1 ? [ operand_1, operand_2.mfrac[1] ] : operand_2.mfrac[1]
				};
			}
			return {
				// 依照規定必須要有<mi>，不可以省略。 e.g., (^12)C
				msup : [ operand_1 || {
					none : null
				}, operand_2 ]
			};

		case '√':
		case '∛':
			// 去除括號 "()"。
			// "√(1+2)" → "<msqrt>1+2</msqrt>"
			if (operand_1.mfenced)
				operand_1 = operand_1.mfenced;
			// (平)方根 / 立方根
			return operator === '√' ? {
				msqrt : operand_1
			} : {
				mroot : [ operand_1, {
					mn : 3
				} ]
			};
		}
		operand_1 = convert_MathML.parse_scalar(operand_1);
		if (!operator)
			return operand_1;
		operand_1 = [ operand_1, {
			mo : operator
		} ];
		if (operand_2)
			operand_1.push(convert_MathML.parse_scalar(operand_2));
		return operand_1;
	}
};

convert_MathML.handler['default'] = convert_MathML.handler.mathml;

// relationships, assignment, equalities
// https://en.wikipedia.org/wiki/Mathematical_operators_and_symbols_in_Unicode
// [≁-⊋]: ≁≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺≻≼≽≾≿⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋
// [⋀-⋭]: ⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭
// TODO: "~"
convert_MathML.RELATIONSHIP_PATTERN = '=><∝≁-⊋⋀-⋭';

// https://en.wikipedia.org/wiki/Plus_and_minus_signs#Character_codes
convert_MathML.non_scalar_chars = '(){}^√∛*\\/⁄∕×⋅÷+\\-−±' + convert_MathML.RELATIONSHIP_PATTERN + ',;\r\n';

// operator : [ pattern, handler, object_to_add ]
// 運算子優先順序最高到最低
(convert_MathML.operator = [
// parentheses
[ /\(([^()]+)\)/, function($0, $1) {
	return [ '()', $1 ];
} ], [ /{([^{}]+)}/, function($0, $1) {
	return [ '{}', $1 ];
} ], [ /\[([^[]]+)\]/, function($0, $1) {
	return [ '[]', $1 ];
} ],
// exponents.
[ /(\S*)\^([+\-−±]?\S+)/, function($0, $1, $2) {
	// [ , base, power ]
	return [ '^', $1, $2 ];
} ], [ /([√∛])([+\-−±]?\S+)/, function($0, $1, $2) {
	// [ , base, power ]
	return [ $1, $2 ];
} ],
// multiplication and fraction.
[ /(\S+)([*\/⁄∕×⋅÷])(\S+)/, function($0, $1, $2, $3) {
	if ($2 === '*')
		$2 = '⋅';
	// [ , numerator, denominator ]
	return [ $2, $1, $3 ];
} ],
// addition and subtraction
[ /(\S+)([+\-−±])(\S+)/, function($0, $1, $2, $3) {
	return [ $2, $1, $3 ];
} ],
// relationships, assignment, equalities
[ new RegExp('(\\S+)([' + convert_MathML.RELATIONSHIP_PATTERN + '])(\\S+)'), function($0, $1, $2, $3) {
	return [ $2, $1, $3 ];
} ],
// terms
[ /\S+(?:,\S+)+/, function($0) {
	($0 = $0.split(',')).unshift(',');
	return $0;
} ],
// terms
[ /\S+(?:;\S+)+/, function($0) {
	($0 = $0.split(';')).unshift(';');
	return $0;
} ] ])
//
.forEach(function(term) {
	// 不可用 'g'! e.g., 2+3-4+5
	term[0] = new RegExp(term[0].source.replace(/\\S/g, '[^'
			+ convert_MathML.non_scalar_chars + ']'), '');
	// library_namespace.debug(term[0]);
});

convert_MathML.RELATIONSHIP_PATTERN = new RegExp('^[' + convert_MathML.RELATIONSHIP_PATTERN + ']$');


convert_MathML.process = function(text, order, queue) {
	library_namespace.debug('[' + text + '] (' + order + ')', 3, 'convert_MathML.process');
	var changed, operator;
	while (true) {
		if (changed)
			changed = false;
		else if (!(operator = convert_MathML.operator[order++]))
			break;
		else {
			library_namespace.debug('shift to ' + operator[0], 3, 'convert_MathML.process');
		}

		text = text.trim()
		//
		.replace(
			operator[0],
			function($0, $1, $2, $3) {
				// return [ type, text1, text2 ]
				var expression = operator[1]($0, $1, $2, $3);
				if (!expression) {
					library_namespace
							.error("convert_MathML.process: Cannot parse: '"
									+ $0 + "'");
					return $0;
				}
				changed = true;

				// next order
				expression.forEach(function(term, index) {
					if (index > 0)
						expression[index] = convert_MathML.resolve(
								convert_MathML.process(term, order, queue),
								queue);
				});

				queue.push(expression);
				return (// queue.separator +
				queue.prefix
				// - 1: get the real index
				+ (queue.length - 1) + queue.postfix
				// + queue.separator
				);
			});
		library_namespace.debug('→ [' + text + ']', 3, 'convert_MathML.process');
	}
	library_namespace.debug('return [' + text + ']', 3, 'convert_MathML.process');
	return text;
};

// parse math expression.
convert_MathML.parse = function(text, queue) {
	if (!queue) {
		queue = [];
		// assert: NOT space or operator.
		queue.prefix = '［';
		queue.postfix = '］';
		while (text.includes(queue.prefix))
			// 維持 open/close quote 相同的長度。
			queue.prefix += '［', queue.postfix = '］' + queue.postfix;
		while (text.includes(queue.postfix))
			queue.prefix += '［', queue.postfix = '］' + queue.postfix;
		// queue.separator = queue.prefix + queue.postfix;

		queue.pattern = '\\' + queue.prefix.split('').join('\\') + '(\\d+)'
		//
		+ '\\' + queue.postfix.split('').join('\\');

		// [ , index ]
		queue.index_pattern = new RegExp('^\\s*' + queue.pattern + '\\s*$');

		// [ , index || '' ]
		queue.pattern = new RegExp(queue.pattern + '|$', 'g');
	}

	// 前期處理。
	// TODO: °º⁺⁻⁼ ⁰¹²³⁴⁵⁶⁷⁸⁹⁽⁾ ±♥´ ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ ₐₑₒₓₔ½ ⅓⅔ ¼¾ ⅕⅖⅗⅘ ⅙⅚ ⅛⅜⅝⅞
	text = text.replace(/!=|<>/g, '≠').replace(/>=/g, '≥').replace(/<=/g, '≤').replace(/⅟/g, '1⁄');

	// TODO: &InvisibleTimes; 用於表示乘法運算中被省略的乘號。
	// https://zh.wikipedia.org/wiki/%E6%95%B0%E5%AD%A6%E7%BD%AE%E6%A0%87%E8%AF%AD%E8%A8%80#Presentation_MathML

	if (false)
		text = text.replace(
		// 處理數學常數。
		// https://en.wikipedia.org/wiki/Mathematical_constant
		// 3^2π → 3^(2⋅π)
		// TODO: &pi;
		/(\d+(?:\.\d+)?)\s*(pi|[a-z\u0370-\u03FF])([^\da-z\u0370-\u03FF]|$)/ig,
				'($1⋅$2)$3');

	text = convert_MathML.process(text, 0, queue);
	// library_namespace.debug('convert_MathML.parse: [' + text + ']');
	// library_namespace.debug('convert_MathML.parse: queue [' + queue + ']');

	text = convert_MathML.resolve(text, queue);
	// library_namespace.debug('convert_MathML.parse: return [' + text + ']');
	// library_namespace.debug('convert_MathML.parse: queue [' + queue + ']');
	return text;
};

// (?:[+\-−±]?\d+(?:\.\d+)?[°∘%％‰‱]?|pi|PI|Pi|[eiKπδφγλΩ∞ℵ])
var PATTERN_numeric = /[+\-−±]?\d+(?:\.\d+)?[°∘%％‰‱]?/;
// [ , 純數, 識別元 ]
convert_MathML.PATTERN_numeric_prefix = new RegExp('^(' + PATTERN_numeric.source + ')([^\d].*)?$' + '$');
convert_MathML.is_numeric_prefix = function(expression) {
	return expression.match(convert_MathML.PATTERN_numeric_prefix);
};

convert_MathML.PATTERN_numeric = new RegExp('^' + PATTERN_numeric.source + '$');
// 傳回 {Boolean}，說明運算式是否可做為數字來評估。
convert_MathML.is_numeric = function(expression) {
	return convert_MathML.PATTERN_numeric.test(expression);
};


// 解開 queue index。
// for: number, queue index, or the combination.
convert_MathML.resolve = function(text, queue) {
	// library_namespace.debug('convert_MathML.resolve: [' + text + ']');
	// library_namespace.debug('convert_MathML.resolve: queue [' + queue.join(';')
	// + ']');

	//function is_index(token) { return token.startsWith(queue.prefix) && token.endsWith(queue.postfix); }

	var matched = text.match(queue.index_pattern);
	if (matched)
		return queue[matched[1]];

	if (convert_MathML.is_numeric(text))
		return text;

	library_namespace
			.debug('convert_MathML.resolve: Parse combinated expression: ['
					+ text + ']');
	var array = [ null ], lastIndex = 0, changed, matched;
	for (queue.pattern.lastIndex = 0;;) {
		matched = queue.pattern.exec(text);
		// 前導 text
		var pre_text = null;
		if (matched.index > lastIndex) {
			pre_text = text.substring(lastIndex, matched.index);
			if (/(?:^\s*[+\-−±]?|\s)\d+(?:\.\d+)?(?:\s|$)/.test(pre_text))
				// e.g., "log 3.3"
				changed = true, Array.prototype.push.apply(array, pre_text
						.split(/\s+/));
			else
				array.push(pre_text);
		}
		lastIndex = queue.pattern.lastIndex;

		// library_namespace.debug([ pre_text, matched[1], lastIndex ]);
		if (matched[1])
			changed = true, array.push(queue[matched[1]]);
		else {
			if (!changed && pre_text && convert_MathML.is_numeric(pre_text))
				changed = true;
			break;
		}
	}
	// if (changed) library_namespace.debug(array);
	return changed ? array : text;
};

// 處理純量與變數。
convert_MathML.parse_scalar = function(text, no_MathML) {
	if (typeof text === 'object') {
		return !no_MathML && Array.isArray(text) ? {
			mrow : text
		} : text;
	}
	if (no_MathML)
		return text;

	if (!(text = String(text).trim()))
		return text;

	if(/\s/.test(text)) {
		text = text.split(/\s+/);
		// 多項。 e.g., "2a 3b 4ac"
		text.forEach(function(term, index) {
			text[index] = convert_MathML.parse_scalar(term);
		});
		return text;
	}

	var is_numeric = convert_MathML.is_numeric(text);
	// 純數。e.g., "1"
	if (is_numeric)
		return {
			mn : text
		};

	// 純數 + 識別元。e.g., "2a", "3.3π"
	if (is_numeric = convert_MathML.is_numeric_prefix(text))
		return [{
			mn : is_numeric[1]
		}, convert_MathML.parse_scalar(is_numeric[2]) ];

	// 下標。e.g., "log_2"
	if (is_numeric = text.match(/^([^_]*)_([^_]+)$/))
		// <msub><mi>x</mi><mi>y</mi></msub>
		return {
			// 依照規定必須要有<mi>，不可以省略。 e.g., (_6)C
			msub : [ convert_MathML.parse_scalar(is_numeric[1]) || {
				// TODO: should use <none />
				none : null
			}, convert_MathML.parse_scalar(is_numeric[2]) ]
		};

	if (library_namespace.is_debug() &&
	// a-zA-Z: normal variable. 變量
	// \u0370-\u03ff: mathematical constant. 數學常數/希臘字母變量. e.g., π
	// \u2E80-\u30000: Unihan variable
	!/^[a-zA-Z\u0370-\u03ff∞ℵ\u2E80-\u30000][a-zA-Z\u0370-\u03ff∞ℵ\u2E80-\u30000\d]*$/
					.test(text))
		library_namespace.error("convert_MathML.parse_scalar: Cannot parse: '"
				+ text + "'");

	// 純識別元。e.g., "x"
	return {
		mi : text
	};
};

// 將 convert_MathML.parse() 之結果，reduce 成所須的格式。
convert_MathML.reduce = function(structure, node, handler) {
	function process_mprescripts(structure, postsuperscript) {
		if (!structure[0]
		&& Array.isArray(structure[1])
		&& structure[1][0] === '()'
		&& structure[2]
		// e.g., (_2), (_2^1), (_(a+b)), (_(a+b)^(c+d))
		&& (Array.isArray(matched = structure[1][1]) ? /^[^_]$/.test(matched[0]) && (!matched[1] || /^_/.test(matched[1])) : !matched || /^_/.test(matched))
		) {
			// <mprescripts />
			// https://developer.mozilla.org/zh-TW/docs/Web/MathML/Element/mmultiscripts
			// e.g., (_2^1)x, (_2^1)x_4^3, (_2^1)x_4^(3)
			var mmultiscripts = convert_MathML.reduce(structure[2], node, handler);
			if (!Array.isArray(mmultiscripts)) {
				if (mmultiscripts && Array.isArray(mmultiscripts.msub)) {
					mmultiscripts = mmultiscripts.msub;
					mmultiscripts.push(postsuperscript && {
						mi : postsuperscript
					} || {
						none : null
					});
				} else {
					mmultiscripts = [ mmultiscripts, {
						none : null
					}, {
						none : null
					} ];
				}
			}
			mmultiscripts.push({
				mprescripts : null
			});
			structure = structure[1][1];
			if (!Array.isArray(structure)) {
				structure = convert_MathML.reduce(structure, node, handler);
				if (structure && Array.isArray(structure.msub)) {
					structure = [ , structure.msub[1], structure.msub[0] ];
				}
			}

			// presubscript
			matched = convert_MathML.reduce(structure[1], node, handler);
			if (!matched) {
				matched = {
						none : null
				};
			} else if (Array.isArray(matched.msub) && matched.msub.length === 2 && ('none' in matched.msub[0])) {
				matched = matched.msub[1];
			}

			mmultiscripts.push(
			// presubscript
			matched,
			// presuperscript
			convert_MathML.reduce(structure[2], node, handler));
			return {
				mmultiscripts : mmultiscripts
			};
		}
	}

	// library_namespace.debug(structure);
	if (!Array.isArray(structure))
		return handler(structure);

	// structure = [ operator, operand_1, operand_2, .. ]

	if ((structure[0] in {
		'()' : true,
		'{}' : true,
		'[]' : true
	})
	// 矩陣。
	&& structure.length === 2 && Array.isArray(structure[1])
			&& structure[1][0] === ',') {
		var matrix = [], i = 0, length = structure[1].length;
		if (structure[1].every(function(operand, index) {
			if (index === 0)
				// pass the operator
				return true;
			// 確認 array 型態相同。
			if (operand[0] !== structure[0])
				return;

			// 確認 operand 為 array。
			if (Array.isArray(operand[1]) && operand[1][0] === ','
			// 確認 array 大小皆同。
			&& (!matrix.length || matrix[0].length === operand[1].length - 1)) {
				// e.g., {{1,2},{3,4}}
				var m = [];
				operand[1].forEach(function(expression, index) {
					if (index > 0)
						m.push(convert_MathML.reduce(expression, node,
								handler));
				});
				// assert: true === !![].push(0)
				return matrix.push(m);
			}

			// 確認 operand 為 array。
			if (typeof operand[1] !== 'object'
			// 確認 array 大小皆同。
			&& (!matrix.length || matrix[0].length === 1))
				// e.g., ((5),(6))
				return matrix.push([ operand[1] ]);

			if (library_namespace.is_debug()) {
				library_namespace.debug('array 型態' + (operand[0] === structure[0] ? '相同' : '不同'));
				library_namespace.debug(Array.isArray(operand[1]) && operand[1][0] === ',' ? 'operand 為 array' : 'operand 為 ' + (Array.isArray(operand[1]) ? operand[1][0] : operand[1]));
				if (matrix[0])
					library_namespace.debug('array 大小: ' + matrix[0].length + ' != ' + (operand[1].length - 1));
			}
		}))
			return handler(matrix, 'm', structure[0]);
	}

	// 前期處理。
	var matched;

	if (structure[0] === '^') {
		if (matched = process_mprescripts(structure[1], structure[2])) {
			return matched;
		}

		// e.g., sin^-1(2π)
		if (Array.isArray(structure[2]) && structure[2].length === 3
				&& !structure[2][0]) {
			structure[2][1] = [ '^', structure[1], structure[2][1] ];
			structure = structure[2];
		}
		// e.g., sin^-1 2π
		if (typeof structure[2] === 'string'
				&& (matched = structure[2]
						.match(/^([+\-−±]?\d+(?:\.\d+)?)\s+(\S+)$/))) {
			structure[2] = matched[1];
			structure = [ , structure, matched[2] ];
		}

	} else if (matched = process_mprescripts(structure)) {
		return matched;
	}

	// TODO: <munderover />, <math>∫_2^4 dy/dx</math>

	structure.forEach(function(operand, index) {
		if (index > 0)
			structure[index] = convert_MathML.reduce(operand, node, handler);
	});
	return structure[0] && !(structure[0] in {
		'{' : true,
		',' : true,
		';' : true
	}) ? handler(structure[1], structure[0], structure[2]) : handler(structure
			.slice(1), structure[0]);
};


return (
	_// JSDT:_module_
);
}


});

