﻿/*******************
Copyright (C) 2007, David Bolton
All rights reserved

Request permission to use script at
http://davidbolton.info/contact
********************/

/*******************
Function to convert normal math notation into javascript math notation
removes white space, interprets implied multiplication, and interprets caret as power operator

http://davidbolton.info/math/notation_converter/
********************/
function GCTBconvertequation(equ) {
	
	// Function to insert asterisks ("*") between every instance of type1 (e.g. number) followed by type2 (e.g. variable)
	function insertMultiplication(equ, type1, type2) {
		var reg_ex = new RegExp(type1+type2); //converts string to regular expression
		var start_index = equ.search(reg_ex);
		while (start_index > -1 && start_index < equ.length) {
			var index = start_index + equ.substring(start_index,equ.length).match(type1)[0].length;
			equ = equ.substring(0,index) + "*" + equ.substring(index,equ.length);
			start_index = equ.substring(index+1,equ.length).search(reg_ex);
			if (start_index > -1) { start_index += index+1; }
		}
		return equ;
	}
	
	
	// Regular expression declarations (initially strings)
	var bracket = "[\(\)\[\]\{\}]";
	var number = "[0-9.]+"; //matches a number of any length 
	var variable = "[x]"; //could possibly be extended [xyzn] or even support for subscripts e.g. [a-z]_\w+
	var func_char = "[a-z√]"; //starting characters for recognized javascript math function e.g. sin, cos, tan, sqrt, etc. could possibly be extended to support √
	var constant = "(E|(LN2)|(LN10)|(LOG2E)|(LOG10E)|(PI)|(SQRT1_2)|(SQRT2))"; // recognized javascript math constants. Could possibly be extended to support π
	var constant_re = new RegExp(constant); //convert string to regular expression
	//var sign = "[=,\*\/-+%\^]";

	// Tidy input
	equ = equ.replace(/\s/g,""); //trim whitespace (using "regular expressions")
	
	//Implied multiplication for right parenthesis
	if (equ.indexOf(")")) { //if statements nonnessary but included for (probably negligable) performance gain)
		equ = equ.replace(/\)\(/g,")*(");
		equ = insertMultiplication(equ, "\\)", number); // (nonstandard) Double slash "\\" needed for when string is converted to reg. expression
		equ = insertMultiplication(equ, "\\)", variable);
		equ = insertMultiplication(equ, "\\)", func_char);
		equ = insertMultiplication(equ, "\\)", constant); // (nonstandard)
	}
	
	// Implied multiplication for numbers
	equ = insertMultiplication(equ, number, "\\(");
	equ = insertMultiplication(equ, number, variable);
	equ = insertMultiplication(equ, number, func_char);
	equ = insertMultiplication(equ, number, constant);
	equ = equ.replace(/LOG10\*E/g,"LOG10E"); //quick fix for the E in LOG10E constant interpreted as a separate constant
	equ = equ.replace(/LOG2\*E/g,"LOG2E"); //quick fix for the E in LOG2E constant interpreted as a separate constant
	
	//Implied multiplication for variables
	equ = insertMultiplication(equ, variable, "\\(");
	equ = equ.replace(/max\*\(/g,"max("); //quick fix for the x in max() function interpreted as a variable	
	equ = insertMultiplication(equ, variable, number); // (nonstandard)
	equ = insertMultiplication(equ, variable, variable);
	equ = insertMultiplication(equ, variable, func_char); //need to look into this
	equ = equ.replace(/ex\*p\(/g,"exp("); //quick fix for the x in exp() function interpreted as a variable
	equ = insertMultiplication(equ, variable, constant);

	//Implied mulitplication for constants
	if (equ.search(constant_re)>-1) {
		equ = insertMultiplication(equ, constant, "\\(");
		equ = insertMultiplication(equ, constant, number);
		equ = insertMultiplication(equ, constant, variable);
		equ = insertMultiplication(equ, constant, func_char);
		equ = insertMultiplication(equ, constant, constant);
	}
	
	//Interpret caret ("^") as power operator
	if (equ.indexOf("^")) {
		equ = segmentOnRight(equ, "^", "^", ")");
		equ = segmentOnLeft(equ, "^", "pow(", ",");
		equ = equ.replace(/LOG10pow\(E/g,"pow(LOG10E"); //quick fix for the E in LOG10E constant interpreted as a separate constant
		equ = equ.replace(/LOG2pow\(E/g,"pow(LOG2E"); //quick fix for the E in LOG10E constant interpreted as a separate constant
	}
	

	// find segment to right of caret and place segment inside pow() function
	function segmentOnRight(equ, sign, nest_lt, nest_rt) {
	    var start_index = equ.indexOf(sign)+1;
		var segment = "";
		while (start_index > 0 && start_index < equ.length) {
			segment = "";
			var index = start_index;
			var ch = equ[index];
			
			if (ch == "-") {//negative sign
				segment = "-";
				ch = equ[index+1]; }
				
			if (ch.match(/[0-9.]/)) {//number
				segment += equ.substring(index,equ.length).match(/[0-9.]+/)[0]; } 
			else if (ch.match(/[x]/)) {//variable
				segment += ch; }
			else if (ch.match(/[a-z]/)) {//function
				segment += equ.substring(index,equ.length).match(/\w+/)[0];//function upto left-parenthesis
				index += segment.length;
				ch = "("; }
			else if (ch.match(/[ELPSπ]/)) {//constant
				segment += equ.substring(index,equ.length).match(constant_re)[0]; }
				
			if (ch == "(") {//parenthesis
				segment += equ.substring(index,equ.length).collectBracketGroup(1); }
			
			equ = equ.substring(0,start_index-sign.length)+nest_lt+segment+nest_rt+equ.substring(start_index+segment.length,equ.length);
			start_index = equ.substring(index,equ.length).indexOf(sign);
			if (start_index > -1) { start_index += index+sign.length; }
		}
		
		return equ;
	}
	function segmentOnLeft(equ, sign, nest_lt, nest_rt) {
	
		//find last match for regular expression--analogous to lastIndexOf()
		String.prototype.lastMatch = function(reg_ex) {
			var start_str = this;
			var str = start_str;
			var match = str.match(reg_ex);
			if (match) str = str.substring(str.search(reg_ex),str.length);
			while (match && str.length>match[0].length) { //find last match
				start_str = str;
				str = str.substring(str.search(reg_ex)+1,str.length);
				match = str.match(reg_ex);
			}
			match = start_str.match(reg_ex);
			return match;
		}
	
	    var start_index = equ.indexOf(sign);
		var segment = "";
		var constant_re = new RegExp(constant); //convert string to regular expression
		while (start_index > 0) {
			var index = start_index;
			var segment = "";
			var ch = equ[index-1];
			
			var constant_match = equ.substring(0,index).lastMatch(constant_re);
			if (constant_match) {//constant
				if (ch == constant_match[0].substring(constant_match[0].length-1,constant_match[0].length)) {
					segment = constant_match[0]; } }
			
			if (ch.match(/[0-9.]/) && !segment) {//number (unless number part of constant, i.e. LN2 and LN10)
				segment = equ.substring(0,index).lastMatch(/[0-9.]+/)[0]; } 
			else if (ch.match(/[x]/)) {//variable
				segment = ch; }
			else if (ch == ")") {//parenthesis
				segment = equ.substring(0,index).collectBracketGroup(-1);
				index -= segment.length;
				ch = equ[index-1];
				
				if (ch.match(/[a-z2]/)) {//function (preceding parentheses segment)
					segment = equ.substring(0,index).lastMatch(/\w+/)[0] + segment; }
			}	

			equ = equ.substring(0,start_index-segment.length)+nest_lt+segment+nest_rt+equ.substring(start_index+sign.length,equ.length);
			start_index = equ.substring(index+nest_lt.length+sign.length,equ.length).indexOf(sign);
			if (start_index > -1) { start_index += index+nest_lt.length+sign.length; }
		}
		
		return equ;
	}
	
	return equ;
}

String.prototype.collectBracketGroup = function(direction) {
	var bracket1 = (direction==1) ? "(" : ")";
	var bracket2 = (direction==1) ? ")" : "(";
	var bracket_ct1 = 1;
	var bracket_ct2 = 0;
	var index = 1;
	while (bracket_ct2 < bracket_ct1 && index<this.length) {
		var ch = (direction==1) ? this[index] : this[this.length-index-1];
		if (ch==bracket1) { bracket_ct1++; }
		else if (ch==bracket2) { bracket_ct2++; }
		index++
	}
	return (direction==1) ? this.substring(0,index) : this.substring(this.length-index,this.length);
}

