// ********** COPYRIGHT (c) 2008, 2009, 2010 STEFAN WANER *************
// *********************** ALL RIGHTS RESERVED ************************


// *** UPDATE NOTES
// Dec 20 2009 added formatLinear()
// april 13 close paren + something in put product
// April 26:
// Changed randomSequenceZero to return a sequence of the correct length// May 11 2010 added function openText

// TO DO
// convertToRadicals(theTexString); 

// *** Error Handler ******
function myErrorTrap(message,url,linenumber) {
this.parent.bottom.window.location = "wrong.html";
return (true);
} // end of on error
// ********************

// *** Bilingual utilities

function getLanguage() {
var outStr = "en"
if (window.location.pathname.toString().indexOf("MundoReal") != -1) outStr = "es";
return(outStr);
} // getLanguage

function writeText() {
if (theLanguage == "en") document.writeln(theText[0]);
else document.writeln(theText[1]);
}

function addText(words,palabras) {
// returns Arr[0] if n else Arr[0]
return((theLanguage=="en")? words:palabras )
}

function writeText(words,palabras) {
// write Arr[0] if n else Arr[0]
document.writeln(addText(words,palabras))
}

// *** end of bilingual utilities

// the following are also in homeworkScripts.js
function randomSequence(n) {
// Returns an array A[i] whose ith entries are a random
// permutation of 1,..,n
var outArr = new Array();
var slot = new Array();
for (var i = 1; i <= n; i++) slot[i] = i;
for (var i = 1; i <= n; i++) {
	var j = randomInt (1,n-i+1); // number of slots left
	outArr [i] = slot [j];
	for (var k = j; k <= n-i; k++) {
		slot[k] = slot[k+1];
		} // k
	} // i
return (outArr);
} // randomSequence


function randomSequenceZero(n) {
// an array whose ith entry is a random perm of 0...n (starts at [0])
var theAr = randomSequence(n+1);
var outArr = [];
for (var i = 0; i <= n; i++) outArr [i] = theAr [i+1]-1;
return (outArr);
} // randomSequenceZero


function pickOne() {
var argArr = pickOne.arguments;
var n = argArr.length-1;
return(pickOne.arguments[randomInt(0,n)]);
}

function randomInt(a,b) {
// returns a random integer in the range [a, b]
return(a + Math.floor(Math.random()*(b-a+1)));
}

function randomNonzeroInt(a, b) {
// returns a nonzero random integer in the range [a, b]
if (a > 0) return(randomInt(a,b));
else {
	var thePick = randomInt(a,b-1);
	if (thePick >= 0) return(thePick+1);
	else return(thePick);
	}
}

function shuffle(A) {
// A must be an array of arrays.
// internally shuffles each of these arrays
var APrime = A.slice();
var len = A.length;
for (var i = 0; i <= len-1; i++) {
	var len_i = A[i].length;
	var Copy = A[i].slice();
	var P = randomSequence(len_i);
	for (var j = 0; j <= len_i-1; j++) {
		APrime[i][j] = Copy[P[j+1]-1];
		} //j
	} // i
// alert(APrime);
return(APrime);
} // shuffle
 
function shuffleArr(A) {
// Just shuffles the array A
var len = A.length;
var Copy = A.slice();
var APrime = new Array();
var P = randomSequenceZero (len-1);
for (var j = 0; j <= len-1; j++) {
	APrime[j] = Copy[P[j]];
	} //j
// alert(APrime);
return(APrime);
} // shuffle

function randomizeNum (number, n, delta) {
// adds delta times a random integer in [-n, n]
return (randomInt(-n,n)* delta + number);
}

function randomizeArr (inArrayName, numPoints, n, delta, minVal, maxVal, rounding) {
// inArrayName needs to be global to the document 
// the array is expected to have the form [ignored, x_1, x_2, ..., x_numPoints]
// adds delta times a random integer in [-n, n] to each entry
// gives minimum and maximum values in slots numPoints + 1, numPoints + 2

var outArray = new Array();

	var theCmd = "outArray = " + inArrayName + ".slice()";
	var doIt = eval(theCmd);
	var xvar = 0;
	var maxValue, minValue;
	for (var i = 1; i <= numPoints; i++) {
		xvar = outArray [i];
		outArray [i] = parseFloat(roundDec(randomizeNum(xvar,n, delta) , rounding));
		if (outArray [i] > maxVal) outArray [i] = maxVal;
		else if (outArray [i] < minVal) outArray [i] = minVal;
		if (i == 1) { maxValue = outArray [i]; minValue = outArray [i]}
		else if (outArray [i] > maxValue) maxValue = outArray [i];
		else if (outArray [i] < minValue) minValue = outArray [i];
		} // i
	outArray [numPoints + 1] = minValue;
	outArray [numPoints + 2] = maxValue;

return(outArray);
}


// *** end of the randomize routines from homeworkScripts.js



function generateMultipleChoiceArray(numChoices, correctCh, A ) {
// A is an array of choice array strings
// so an item is a concatenoation of random choices from each array
// eg A = [ ['red', 'green'] , ['mustang', 'pontiac'] ]
// will generate four possible choices
// zeroth position of output array is the index of the correct choice

var outArray = new Array();
// first pick a slot for the correct answer, if nonempty
if (correctCh != "") {
	var k = randomInt(1,numChoices);
	outArray[k] = correctCh;
	outArray[0] = k;
	} 
var B = permute(shuffle(A)); 
// alert(B);
	// the set of all permutations of a shuffled
	// version of A
var counter = 1;
for (var i = 1; i <= numChoices; i++) {
	if (i == k) i += 1;
	if (B[counter] == correctCh) {
		counter += 1;
		outArray[i] = B[counter]}
	else (outArray[i] = B[counter]);
	counter += 1;
	} // i
// alert(outArray);
return (outArray);
} // generateMultipleChoiceArray


function sesame(url,hsize,vsize){ 
// Default size is 550 x 400
        var tb="toolbar=0,directories=0,status=0,menubar=0"
        tb+=",scrollbars=1,resizable=1,"
    var tbend="width="+hsize+",height="+vsize;
    if(tbend.indexOf("<undefined>")!=-1){tbend="width=550,height=400"}
        tb+=tbend
        Win_1 = window.open("","win1",tb);
        Win_1 = window.open(url,"win1",tb);
	Win_1.focus();
    }


// **** openText *****************
function openText(theText,title,theColor, hsize,vsize){ 
// Default size is 550 x 400
var tb="toolbar=0,directories=0,status=0,menubar=0"
tb+=",scrollbars=1,resizable=1,"
var tbend="width="+hsize+",height="+vsize;
if(tbend.indexOf("<undefined>")!=-1){tbend="width=550,height=400"}
tb+=tbend
var theString = '<html><head><LINK REL=stylesheet HREF="' + pathPrefix + 'homework/finiteTutStyles.css" TYPE="text/css">';
theString += '<STYLE TYPE="text/css">.MATH {font-family: Times; font-size: 12px; font-style: italic; }.MATHOVER {font-family: Times; font-size: 12px; text-decoration: overline}</STYLE>';
theString += '<LINK REL=stylesheet HREF="' + pathPrefix + 'typesetting/mathTypesettingStyles.css" TYPE="text/css">';
theString += "</head><title>" + title + "</title><body bgcolor = " + theColor + ">" + theText + "</html>";
Win_1 = window.open("","win1",tb);
Win_1.document.close();
Win_1.document.open();
Win_1.document.write(theString);
Win_1.focus();
    }
// ***************************

// *** Array max, min, hcf, sum

Array.prototype.sum = function() {
  return (! this.length) ? 0 : this.slice(1).sum() +
      ((typeof this[0] == 'number') ? this[0] : 0);
};


Array.prototype.max = function() {
var max = this[0];
var len = this.length;
for (var i = 1; i < len; i++) if (this[i] > max) max = this[i];
return max;
}
Array.prototype.min = function() {
var min = this[0];
var len = this.length;
for (var i = 1; i < len; i++) if (this[i] < min) min = this[i];
return min;
}


Array.prototype.hcf = function() {
var theMax = this.max();
var len = this.length;
var theHcf = 1;
if (theMax == 0) theHcf = 0;
// get smallest non-zero element
var theMin = 0;
for (var i = 0; i < len; i++) {
	if ((theMin == 0) && (this[i] != 0)) theMin = Math.abs(this[i]);
	else if ((this[i] != 0) && (Math.abs(this[i]) < theMin)) theMin = Math.abs(this[i]);
	}
var isFactor = false;
for (var n = theMin; n >= 1; n--) {
	isFactor = true;
	for (j = 0; j < len; j++) {
// alert(this[j]%n)
		if (this[j]%n != 0) isFactor = false;
		}
	if (isFactor) {
		theHcf = n;
		n = 0;
		}
	}
return theHcf;
}

// ********************Old Cookie Routines **********************
var cookiesEnabled = false;
var now = new Date();
var oneDay = new Date(now.getTime() + 60000 * 60 * 24);
var oneYear = new Date(now.getTime() + 60000 * 60 * 24 * 365);
var yesterday = new Date(now.getTime() - 60000 * 60 * 24);

function cookieTest() {
// returns false if cookies are not enabled
var now = new Date();
var twosecs = new Date(now.getTime() + 1000 * 2)
setCookie('cookieTest','You will meet a tall dark stranger.', twosecs);
var testing = getCookie('cookieTest');
if(testing == null) {cookiesEnabled = false; return(false) }
else {cookiesEnabled = true; return(true) }
} // cookieTest

function setCookie (cookieName, cookieValue, expires, path, domain, 
secure) {
  document.cookie = 
    escape(cookieName) + '=' + escape(cookieValue) 
    + (expires ? '; EXPIRES=' + expires.toGMTString() : '')
    + (path ? '; PATH=' + path : '')
    + (domain ? '; DOMAIN=' + domain : '')
    + (secure ? '; SECURE' : '');
}

function getCookie (cookieName) {
  var cookieValue = null;
  var posName = document.cookie.indexOf(escape(cookieName) + '=');
  if (posName != -1) {
    var posValue = posName + (escape(cookieName) + '=').length;
    var endPos = document.cookie.indexOf(';', posValue);
    if (endPos != -1)
      cookieValue = unescape(document.cookie.substring(posValue, 
endPos));
    else
      cookieValue = unescape(document.cookie.substring(posValue));
  }
  return cookieValue;
}

// ***************** End of Old Cookie Routines *****************

function leftChop (InString, num)  {
	OutString=InString.substring (num, InString.length);
	return (OutString);
}

function rightChop (InString, num)  {
	var OutString=InString.substring (0, InString.length-num);
	return (OutString);
}

function stripSpaces (InString)  {
	OutString=""; 
	if (InString == "") return (OutString);
	for (Count=0; Count < InString.length; Count++)  {
		TempChar=InString.substring (Count, Count+1);
		if (TempChar!=" ")
			OutString=OutString+TempChar;
	}
	return (OutString);
}

function stripChar (InString,symbol)  {
	OutString="";
	for (Count=0; Count < InString.length; Count++)  {
		TempChar=InString.substring (Count, Count+1);
		if (TempChar!=symbol)
			OutString=OutString+TempChar;
	}
	return (OutString);
}

function stripChars (InString,symbolString)  {
	OutString="";
	for (Count=0; Count < InString.length; Count++)  {
		TempChar=InString.substring (Count, Count+1);
		if (symbolString.indexOf(TempChar)== -1)
			OutString=OutString+TempChar;
	}
	return (OutString);
} // stripChars


function replaceChar (InString,oldSymbol,newSymbol)  {
	OutString="";
	for (Count=0; Count < InString.length; Count++)  {
		TempChar=InString.substring (Count, Count+1);
		if (TempChar!=oldSymbol)
			OutString=OutString+TempChar
		else OutString=OutString+newSymbol;
	}
	return (OutString);
}

function leftTrim(InString)  {
for (var i=0; i <= InString.length; i++)  {
	var TempChar=InString.charAt(i);
	if (TempChar!=" ") {
		return(InString.substring(i,InString.length))
		}
	}
}

function capLetter(num) {
// returns A, B, C, ...
return(String.fromCharCode(64+num))
}

function makeArray3 (X,Y,Z)
	{
	var count;
	this.length = X+1;
	for (var count = 1; count <= X+1; count++)
		// to allow starting at 1
		this[count] = new makeArray2(Y,Z);
	} // makeArray3


function makeArray2 (X,Y)
	{
	var count;
	this.length = X+1;
	for (var count = 0; count <= X+1; count++)
		// to allow starting at 1
		this[count] = new makeArray(Y);
	} // makeArray2


function makeArray (NumElements, Fill)  {
	var Count;
	this.length = NumElements;
	for (Count = 1; Count <= NumElements; Count++)  {
		this[Count] = Fill;
	}
	return (this);
}

function checkString(InString,subString,backtrack)
// check for subString
// if backtrack = false, returns -1 if not found, and left-most location in string if found
// if backtrack = true, returns -1 if not found, and right-most location in string if found
// note that location is to the left of the substring in both cases
{
var found = -1;
var theString = InString;
var Length = theString.length;
var symbLength = subString.length;
for (var i = Length- symbLength; i >-1; i--)
	{	
	TempChar=theString.substring (i, i+ symbLength);
	if (TempChar == subString) 
			{
			found = i;
			if (backtrack) i = -1
			}
	} // i
return(found);
} // check

function replaceSubstring (InString,oldSubstring,newSubstring)  {
	OutString="";
	var sublength = oldSubstring.length;
	for (Count=0; Count < InString.length; Count++)  {
		TempStr=InString.substring (Count, Count+sublength);
		TempChar=InString.substring (Count, Count+1);
		if (TempStr!= oldSubstring)
			OutString=OutString+TempChar
		else 
			{
			OutString=OutString+ newSubstring;
			Count +=sublength-1
			}

	}
	return (OutString);
} 

function parser (InString, Sep)  {
	NumSeps=1;
	for (Count=1; Count < InString.length; Count++)  {
		if (InString.charAt(Count)==Sep)
			NumSeps++;
	}
	parse = new makeArray (NumSeps+4, "");		// my adjustments
	Start=0; Count=1; ParseMark=0;
	LoopCtrl=1;
	while (LoopCtrl==1)  {
		ParseMark = InString.indexOf(Sep, ParseMark);
		TestMark=ParseMark+0;
		if ((TestMark==0) || (TestMark==-1)){
			parse[Count]= InString.substring (Start, InString.length);
			LoopCtrl=0;
			break;
		}
		parse[Count] = InString.substring (Start, ParseMark);
		Start=ParseMark+1;
		ParseMark=Start;
		Count++;
	}
	parse[0]=Count;
	return (parse);
}

function winopen(){
// opens a window in the bottom frame
	var str = winopen.arguments[0];
	var loc = winopen.arguments[1];
	if (navigator.appName == "Microsoft Internet Explorer") this.parent.bottom.window.location = str;
	else window.open(str,loc);
} // winopen

function looksLikeANumber(theString) {
// returns true if theString looks like it can be evaluated
var result = true;
var length = theString.length;
var x = ""
var y = "1234567890-+/*. "
var yLength = y.length;
for (var i = 0; i <= length; i++)
	{ 
	x = theString.charAt(i);
		result = false;
		for (var j = 0; j <= yLength; j++) 
			{
			if (x == y.charAt(j)) {result = true; break}
			} // j
	if (result == false) return(false);
	} // i
return(result);
} // looks like a number

// ********* FUNCTION PARSER ***********

function evaluateFunction(theFunction, theValue, numPlaces, isSigDigits) {
// isSigDigits true gives accurate to numPlaces number of sig digits
// else accuracy numPlaces of decimal places
x = parseFloat(theValue);
var theY = myEval(theFunction);
if (isSigDigits) return parseFloat(roundSigDig(theY, numPlaces));
else return parseFloat(roundDec(theY, numPlaces));
}// 

function evalFuncQuick(theFunction, theValue) {
// no attempt to control accuracy etc
x = parseFloat(theValue);
return(myEval(theFunction));
}//

function myEval(theString)
{
// alert(myParse(theString));
var e = Math.exp(1);
var pi = Math.PI;
return(eval(myParse(theString)))
}

// The above function requires all the following routines
function breakApart(InString) {
	// ******* Input: any string such as aaa*bbb or (aa*aa)*bbb
	// *** This Routine Retuns an array: [operation, left part, right part]
	// *** bbb starts at the left-most operation *, /,  - or +
	// *** if it sees a paren, it stops after closure.

	theString = InString;
	var Length = theString.length;
	var outArray = new Array();
	outArray[1] = theString;
	outArray[2] = "";
	var parenCount = 0;
	var parenWatch = false;
	var looking = true;
	
	
	if (theString.substring (0,1) == "(")
		{
		parenCount++;
		parenWatch = true;
		looking = false;
		}
	// Look for operators
	
	for (Count=1; Count < Length; Count++)  
		{
		TempChar=theString.substring (Count, Count+1);
		if (TempChar == "(") 
			{parenCount ++;}
		else if (TempChar == ")") {parenCount = parenCount-1};

		if ((parenCount == 0) && (parenWatch == true))
			 {
			parenWatch = false;
			outArray[1] = theString.substring (0, Count+1);
			outArray[2] = theString.substring (Count+1, Length);
			}

		if (looking == true)
			{
		if (isCharHere (TempChar, "*/-+)") )

				{ 
				
					{
					// alert(Count);
					looking = false;
					outArray[0] = TempChar;
					outArray[1] = theString.substring (0, Count);
					outArray[2] = theString.substring (Count, Length);
		
					// alert (outArray[1]);
					// alert(outArray[2]);
					} // end if hit one
				} 

			} // end if looking

		} // end of loop


	return (outArray);

} // end of breakApart

function isNumberChar (InString)  {
	if(InString.length!=1) 
		return (false);
	RefString="1234567890)";
	if (RefString.indexOf (InString, 0)==-1) 
		return (false);
	return (true);
}

function isCharHere (theChar, RefString)  {
	if(theChar.length!=1) 
		return (false);
	if (RefString.indexOf (theChar, 0)==-1) 
		return (false);
	return (true);
}



function isNumeric (sText, extraAllowedChars) {
   var ValidChars = "0123456789." + extraAllowedChars;
   var IsNumber=true;
   var Char;

 
   for (i = 0; i < sText.length && IsNumber == true; i++) 
      { 
      Char = sText.charAt(i); 
      if (ValidChars.indexOf(Char) == -1) 
         {
         IsNumber = false;
         }
      }
   return IsNumber;
  
   }

function isNumericParen(inStr){
// returnes true for (###)
// needs to be tested 
return(inStr.search(/\^[\(][0-9.]+[\)]?/));

} //isNumericParen

function putProduct(InString) {
InString = stripSpaces(InString); 
// very important bc some routines just use this

var OutString='', TempChar = '';
// first watch out for e+/-n in floats
// note that things like 2e-5 are ambiguous
// we will take it to mean 2*10^-5 so no * gets put there if not already there
InString = replaceSubstring(InString, "e+", "j+");
InString = replaceSubstring(InString, "e-", "j-");
var knownFns = ["sin", "cos", "exp", "ln", "log", "sqrt", "abs"];
var lenKF = knownFns.length-1;
for (var i = 0; i <= lenKF; i++) InString = replaceSubstring(InString, "x"+ knownFns[i], "x*" + knownFns[i]);

for (Count=0; Count < InString.length; Count++)  {
	TempChar=InString.substring (Count, Count+1);
	if (!isCharHere(TempChar,"xXyYzZeslcapdDuUhHtTlM(") || (Count == 0) )
		{OutString=OutString+TempChar}
	else 
		{
		if (isNumberChar(InString.substring(Count-1,Count)))
			{OutString=OutString+"*"+TempChar}
		else OutString=OutString+TempChar
		}
	}

// added April 13
// now put things products after certain closed parenthesis
for (var Count = 0; Count <= OutString.length-2; Count ++) {
	if (OutString.substring (Count, Count+1) == ")") {
		var TempChar = OutString.substring (Count+1, Count+2)
		if (!isCharHere(TempChar,"+-/*^)")) OutString = OutString.substring (0, Count+1) + "*" + OutString.substring (Count+1, OutString.length)
// alert(OutString)
		}

	} // i
OutString = replaceSubstring(OutString, "j+", "e+");
OutString = replaceSubstring(OutString, "j-", "e-");
return (OutString);
}


function reverse (InString)  {
	OutString="";
	var Length = InString.length;
	for (Count=Length; Count > -1; Count--)  {
		TempChar=InString.substring (Count, Count+1);
		if (TempChar == "(") {TempChar = ")"}
		else if  (TempChar == ")") {TempChar = "("}
		OutString=OutString+TempChar;
		}
	return (OutString);
	}

function powFix2(InString) {
	// ****Replaces one "^" by "pow"
	theString = InString;
	var Length = theString.length;
	outString = theString; 
		// in case nothing happens
	
		// Look for wedge
	var looking = true;
	for (Count=0; Count < Length; Count++)  
		{
		if (looking)
			{
			TempChar=theString.substring (Count, Count+1);
			if (TempChar == "^")
				{
				looking = false;
				rightStr = theString.substring (Count+1,Length);
				leftStr = theString.substring (0,Count);
				// deal with right-hand string
				Aray = breakApart(rightStr);
				Arg2 = Aray[1];
				rightRest = Aray[2];
			
				backString = reverse(leftStr);
				Aray = breakApart(backString);
				Arg1 = reverse(Aray[1]);
				leftRest = reverse(Aray[2]);

// now deal with some fractional exponents		
				var slashIndex = Arg2.indexOf("/");
				if (slashIndex >1) {
					var theNumerator = Arg2.substring(1, slashIndex);
					var theDenominator = Arg2.substring(slashIndex+1,Arg2.length-1);
					if ((parseInt(theNumerator)%2 != 0)&& (parseInt(theDenominator)%2 != 0)) {
						outString = leftRest+"(Math.pow(Math.pow(Math.abs("+Arg1+"),"+ theNumerator +"),1/" + theDenominator + ")*Math.abs("+Arg1+")/"+Arg1+")" +rightRest;
						} // odd radical
					else {
					outString = leftRest+"Math.pow(Math.pow("+Arg1+","+ theNumerator +"),1/" + theDenominator + ")" +rightRest;

//alert(outString);
						}
					}

			
				else outString = leftRest+"Math.pow("+Arg1+","+Arg2+")"+rightRest;
//alert(Arg1);	
				} // end if hit a wedge

			} // end of looking for a wedge
		} // end of loop

// ****** testing *******
// document.Extra.diagnostics.value += outString + cr;
// ***** end testing *****

return (outString);

} // end of powFix2


function powCheck(InString) {
	// ****checks for ^
	
	theString = InString;
	var Length = theString.length;
	
	// Look for wedge
	var found = false;
	for (Count=0; Count < Length; Count++)  
		{
		TempChar=theString.substring (Count, Count+1);
		if (TempChar == "^")
			{
			found = true;
			} // end if hit a wedge
		} // end of looking for a wedge
	return(found);


} // end of powCheck

function myParse(expression)
{
// alert('here')
		var theString = stripSpaces(expression);
		theString = standardizeParens(theString);
		if (theString.indexOf("|") > -1) theString = replaceAbsVals(theString);
		if (theString.indexOf("!") > -1) theString = replaceFactorials(theString);
		theString = replaceSubstring(theString,"--","+");
		with (Math)
			{
		// now convert formatting from GC formatting		
		theString = putProduct(theString);
		theString = replaceSubstring (theString,"exp","Math.exp");
		theString = replaceSubstring (theString,"cos","Math.cos");
		theString = replaceSubstring (theString,"sin","Math.sin");
		theString = replaceSubstring (theString,")(",")*(");
		theString = replaceSubstring (theString,")ln",")*ln");
		theString = replaceSubstring (theString,")abs",")*abs");
		theString = replaceSubstring (theString,"sqrt","Math.sqrt");
		theString = replaceSubstring (theString,"x(","x*(");
		theString = replaceSubstring (theString,")x",")*x");
		theString = replaceSubstring(theString,"log","(1/Math.log(10))*Math.log");
		theString = replaceSubstring(theString,"ln","Math.log");
		theString = replaceSubstring(theString,"abs","Math.abs");
			while (powCheck(theString))
				{
				theString = powFix2(theString);
				// alert(theString);
				}
		theString = theString.toLowerCase();
		theString = replaceSubstring (theString,"math.","Math."); 
		// in case the user put in a math. something
// alert(theString);
			} // with Math

		return(theString);
} // myParse


function standardizeParens(theString) {
	var outStr = replaceChar(theString,"[","(");
	outStr = replaceChar(outStr,"]",")");
	outStr = replaceChar(outStr,"{","(");
	outStr = replaceChar(outStr,"}",")");
	return(outStr);
} // standardizeParens

function replaceAbsVals (theString) {
// does not permit abs within abs --- this is ambiguous anyway, eg |a|b|c|
	while(theString.indexOf("|") > -1) {
		theString = theString.replace("|","abs(");
		theString = theString.replace("|",")");
		}
	return(theString);
} // replaceAbsVals

function replaceFactorials(theString) {
return theString.replace(/([0-9]+)(\!)/g,"fact($1)")

} // replaceFactorials(theString)

// ******** END FUNCTION PARSER **************


// toFraction in pieces routine

function isBad(theNum) {
if ((isNaN(theNum)) || (theNum == Infinity) || (theNum == -Infinity) || (theNum > 10000000000) || (theNum < -10000000000)) return(true);
else return(false)
}

function toFrac(x) {
return toFracArr(x,10000)[3]
}

function toFracArr(x, maxDenom) {
	if (isBad(x)) return x;
	var theFrac = new Array();
	theFrac[1] = 0; // will be numerator
	theFrac[2] = 0; // will be denominator
	theFrac[3] = ''; // will be the fraction string
	theFrac[4] = ''; // will be teX fraction
	var p1 = 1;
	var p2 = 0;
	var q1 = 0;	
	var q2 = 1;	
	var u =0;
	var t = 0;
	var flag = true;
	var negflag = false;
	var a = 0;
	var xIn = x; // variable for later
	var n = 0;
	var d = 0;
	var p = 0;
	var q = 0;

	if (x >10000000000) return(theFrac);
while (flag)
	{
	if (x<0) {x = -x; negflag = true; p1 = -p1}
	var intPart = Math.floor(x);
	var decimalPart = roundSigDig((x - intPart),15);

	x = decimalPart;
	a = intPart;
	
	t = a*p1 + p2;
	u = a*q1 + q2;
	if  ( (Math.abs(t) > 10000000000 ) || (u > maxDenom ) ) 
		{
			n = p1;
			d = q1;
			break;
		}

		p = t;
		q = u;
			
//		cout << "cf coeff: " << a << endl; // for debugging
//		cout << p << "/" << q << endl;	// for debugging
		
	if ( x == 0 )
		{
		n = p;
		d = q;
		break;
		}

		p2 = p1;
		p1 = p;
		q2 = q1;
		q1 = q;
		x = 1/x;
	
	} // while ( true );
	
	theFrac[1] = n;
	theFrac[2] = d;
	var theFracString = '';
	var theTexString = '';
	if (n == 0) { 
		theFracString = "0";
		theTexString = "0";
		}
	else if (d == 1) 
		{
		theFracString = n.toString();
		theTexString = n.toString();
		}
	else if (n < 0) {
		theFracString = "-" + (-n).toString() + "/" + d. toString();
		theTexString = "-" + unescape('%5C') + "frac{" + (-n).toString() + "}{" + d. toString() + "}";
		}
	else {
		theFracString = n.toString() + "/" + d. toString();
		theTexString = unescape('%5C') + "frac{" + n.toString() + "}{" + d. toString() + "}";
		}
	theFrac[3] = theFracString;
	theFrac[4] = theTexString;

	return(theFrac);

} // toFracArr


// ********* Answer checking routines *********
// the following two routines are can be used in both tutorials and 
// review exercises to do answer checking and "peeking"
// handles both strings and numerical values.

// These routines use one or two other string utilities in this file
// example of html code that uses this:
// 
// <form name = "Form1">
//  <input type=text size=35 value="" name="Q1a">
// <input type="button" value = "Check" onClick = 
// "if (checkAnswer('Form1','Q1a',3,3.1419)) winopen('correct.html','bottom'); 
// else winopen('wrong.html','bottom') ">
// <input type="button" value = "Peek" onClick="togglePeek('Form1','Q1a', '3.1419')">
//



function checkAnswerandValue(formName,fieldName,correctAnswer,minLength, maxLength, maxTolerance, assig1,assig2,assig3, assig4) {
// use this to check for simplified expressions
// evaluates the input string in the specified form 
// & returns 1 if the answer is correct (agrees with correctAnswer)
// to within 0.00005 and is the right length otherwise set "maxerror" = whatever
// as one of the assignments
// returns 2 if the answer is right but string too long (unsimplified)
// returns 3 if answer is right but string too short 
// returns 4 if the answer is too small by 3.3 (value of C in integraion)
// returns 0 if the answer is blank 
// else returns 6;
// it evalutes the input string at sthe specified values 
// Each assigi has the form "x=2" or "h=2.3" 
// minLength & maxLength are the permissable ranges of an input string\
// maxTolerance is the largest possible error
// NOTE: requires GLOBAL variables x, h, or whatever is assigned

var theString = "";
var maxerror = maxTolerance; 	
var a1 = eval("theString = stripSpaces(document."+ formName + "." + fieldName + ".value"+")" );
if (assig1 != "") var b1 = eval(assig1);  // set the values equal to numbers
if (assig2 != "") var b2 = eval(assig2);
if (assig3 != "") var b3 = eval(assig3);
if (assig4 != "") var b4 = eval(assig4);

if (theString == "") return(0);

else {
	theString = stripChar (theString, "$");		// for dollar signs
	theString = stripChar (theString, ",");		// for longer numbers
	theString = theString.toLowerCase();
	var val = myEval (theString);
	var correctVal = myEval(correctAnswer);
	var err = Math.abs(val - correctVal);
	var err2 = Math.abs(val + 3.3 - correctVal);	
	// for C with value 3.3 (integrals)
	if (err < maxerror)
		{
		if (theString.length > maxLength) return(2);
		else if  (theString.length < minLength) return(3);
		else return(1);
		}
	
	else if ( err2 < maxerror)
		{
		return(4);
		}

	else return (6);
	} // endif non-empty
} // end checkAnswerandValue



function togglePeek(formName,fieldName,expression) {
// displays the given expression in formName.fieldname if not already visible
// if already visible, it hides it

var theCommand = "";
var theString = "if (document." + formName + "." + fieldName + ".value != '" + expression + "') document." + formName + "." + fieldName + ".value = '" + expression + "';  else document." + formName + "." + fieldName + ".value = '';"
// alert(theString)
var act1 = eval(theString);
return(true);
}

function checkList() {
// format inString, correctAnsString, accuracy, actions

var theRetValue = 1; // returned value
var theArgs = checkList.arguments;
var inString = stripSpaces(theArgs[0]);
inString = inString.toLowerCase();
if (inString == '') theRetValue = 0;

for (var i = 3; i <= theArgs.length-1; i++) {
// alert(theArgs[i]);
eval(theArgs[i])
}

var ansString = theArgs[1];
var acc = theArgs[2];


var P1 = parser(inString,",");
var P1Ans = parser(ansString,",");
if (theRetValue == 1) {
	if (P1[0] < P1Ans[0]) theRetValue = 2; // too few terms
	else if (P1[0] > P1Ans[0]) theRetValue = 3; // too many terms
	else {
		for (var i = 1; i <= P1[0]; i++) {

			var delta = Math.abs(parseFloat(P1[i]) - parseFloat(P1Ans[i]));
// alert(delta);


			if ( (delta > acc) || (isNaN(delta)) ) theRetValue = 6;
			} // i 
		}
	} // if retVal = 1
return(theRetValue);

} // checkList

function checkPairList () {
// checks a list of pairs (x1,y1), (x2,y2) ...
// format inString, correctAnsString, accuracy, actions
// eg 'x = .1';
// returns 1 for correct ans, 0 for blank, 2 for too few terms
// 3 for too many terms
// 6 for incorrect ans 
var theRetValue = 1;
// 
var theArgs = checkPairList.arguments;
var inString = stripSpaces(theArgs[0]);
inString = inString.toLowerCase();
if (inString == '') theRetValue = 0;

for (var i = 3; i <= theArgs.length-1; i++) {
// alert(theArgs[i]);
eval(theArgs[i])
}

var ansString = theArgs[1];
var acc = theArgs[2];
// first convert the strings into the form --&--&--;
inString = replaceSubstring(inString.substring(1,inString.length-1), "),(","&");
// alert(inString);
ansString = replaceSubstring(ansString.substring(1, ansString.length-1), "),(","&");


var P1 = parser(inString,"&");
var P1Ans = parser(ansString,"&");
if (theRetValue == 1) {
	if (P1[0] < P1Ans[0]) theRetValue = 2; // too few terms
	else if (P1[0] > P1Ans[0]) theRetValue = 3; // too many terms
	else {
		for (var i = 1; i <= P1[0]; i++) {
			var P2 = parser(P1[i],",");
			var P2Ans = parser(P1Ans[i],",");

// alert(P2[1] + "," + P2Ans[1]);
			var delta = Math.abs(parseFloat(P2[1]) - parseFloat(P2Ans[1]));
// alert(delta);

			var delta2 = Math.abs(parseFloat(P2[2]) - parseFloat(P2Ans[2]));
			if ( (delta > acc) || (delta2 > acc) || (isNaN(delta)) || (isNaN(delta2)) ) theRetValue = 6;
			} // i 
		}
	} // if retVal = 1
return(theRetValue);
}

// ************** end of answer handling routines *************

// * number routines
function shiftRight(theNumber, k) {
	if (k == 0) return (theNumber)
	else
		{
		var k2 = 1;
		var num = k;
		if (num < 0) num = -num;
		for (var i = 1; i <= num; i++)
			{
			k2 = k2*10
			}
		}
	if (k>0) 
		{return(k2*theNumber)}
	else 
		{return(theNumber/k2)}
	}

function roundSigDig(theNumber, numDigits) {
	with (Math)
		{
		if (theNumber == 0) return(0);
		else if(abs(theNumber) < 0.000000000001) return(0);
// WARNING: ignores numbers less than 10^(-12)
		else
			{
			var k = floor(log(abs(theNumber))/log(10))-numDigits+1
			var k2 = shiftRight(round(shiftRight(abs(theNumber),-k)),k)
			if (theNumber > 0) return(k2);
			else return(-k2)
			} // end else
		}
} // roundSigDig

function roundDec(theNumber, numPlaces) {
with (Math)
	{
	var x =shiftRight(round(shiftRight(theNumber,numPlaces)),-numPlaces);
	return x;
	} // with math
} // roundDec


function roundToNearest(inNum, n) {
// rounds in to the nearest n
return(  Math.round(inNum /n)*n );

}

function addCommas(inNum)
// in goes a number out goes a string
{
if (inNum < 10000) return(inNum.toString());
else {
	var nStr = inNum.toString();
	nStr += '';
	var xyz = nStr.split('.');
	var x1 = xyz [0];
	var x2 = xyz.length > 1 ? '.' + xyz [1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return x1 + x2;
	}
}

// note the next two are different (older) routines
function hcf (a,b) {
var bigger = Math.abs(a);
var smaller = Math.abs(b);
var x = 0;
var theResult = 1;
if ( (a == 0) || (b == 0) ) return(1);
if (smaller > bigger) {x = bigger; bigger = smaller;  smaller = x}
var testRatio = roundSigDig(bigger/smaller, 11);
var testRatio2 = 0;
if (testRatio == Math.floor(testRatio) ) return (smaller)
else
	{
	// look for a factor of the smaller, deplete it by that factor and multiply bigger by it
	var found = false;
	var upperlimit = smaller;
	for (var i = upperlimit; i >= 2; i--)
		{
		testRatio = roundSigDig(smaller/i, 10);
		testRatio2 = roundSigDig(bigger/i, 10);
		if  ( (testRatio == Math.floor(testRatio) ) && (testRatio2 == Math.floor(testRatio2) ) )
			{
			smaller = Math.round(smaller/i);
			smaller = Math.round(bigger/i);
			return(theResult *hcf(bigger, smaller) );
			}
		}
		return(theResult);
		}
alert("error!");
return(-1); // should never get here
} // hcf

function lcm(a,b) {
// lowest common multiple
var bigger = Math.abs(a);
var smaller = Math.abs(b);
var x = 0;
if ( (a == 0) || (b == 0) ) return(0);
if (smaller > bigger) {x = bigger; bigger = smaller;  smaller = x}

var testRatio = roundSigDig(bigger/smaller, 11)
if (testRatio == Math.floor(testRatio) ) return (bigger)
else
	{
	// look for a factor of the smaller, deplete it by that factor and multiply bigger by it
	var found = false;
	for (var i = 2; i <= smaller; i++)
		{
		if (i*i > smaller) break;
		testRatio = roundSigDig(smaller/i, 11);
		if (testRatio == Math.floor(testRatio) )
			{
			smaller = testRatio;
			bigger = bigger*i;
			return( lcm(bigger, smaller) );
			}
		}
		return(bigger*smaller);
		}
return(-1); // should never get here
} // lcm

// Lowest Common Multiple of an array(function lcm)
//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/math/mmc [rev. #1]

mmc = function(o){
    for(var i, j, n, d, r = 1; (n = o.pop()) != undefined;)
        while(n > 1){
            if(n % 2){
                for (i = 3, j = Math.floor(Math.sqrt(n)); i <= j && n % i; i += 2);
                d = i <= j ? i : n;
            }
            else
                d = 2;
            for(n /= d, r *= d, i = o.length; i; !(o[--i] % d) && (o[i] /= d) == 1 && o.splice(i, 1));
        }
    return r;
};


function HideContent(d) {
document.getElementById(d).style.visibility = "hidden"	;
}
function ShowContent(d) {
document.getElementById(d).style.visibility = "visible";
}

function ToggleVisibility(d) {
// alert(document.getElementById(d).style.visibility);
if (document.getElementById(d).style.visibility == "hidden") document.getElementById(d).style.visibility = "visible";
else document.getElementById(d).style.visibility = "hidden";
}

// **************** generation and formating of random functions

function createPoly(degree, shift, coeffType, variableName, complexity,positiveCoeffs) {
// coeffType 1: integer; 2: fraction; 3: decimal
// variableName "x", "t" etc or "random"
// complexity integer part = 0, 1, 2, 3, 4, 5 effects number of sig digits in decimals and size of denominators in fractions
// the decimal value of complexity = number of terms
// eg complexity 3.2 gives a complexity 3 poly with 2 terms
// degree must be positive 
// shift is subtracted from the degree 
// this is stored in 103 entry and nothing more in create
// eg degree 2.3 starts at x^(3-2) and descends
// complexity = 0 gives a monic as well and is otherwise same as complexity 1
// returns an array [0] term is 0 (depth of this structure)
// [1] term = "P" for polynomial
// [2] term is a (big) coefficient matrix whose [i] term is the
// coeff of x^i. Its [100] term is the degree and [101] the
// coefficient type and its [102] entry is the variable name
// The [103] term of the coeff matrix is the exponent negative shift
// if positiveCoeffs is true then all coeffs will be positive
// however this is only implemented for integer and rational coeffs
var monic = false;
var numTerms = 0;
var maxNumTerms = Math.round(10*(complexity - Math.floor(complexity)));
if (maxNumTerms == 0) maxNumTerms = 666; // none specified
// alert(maxNumTerms);
complexity = Math.floor(complexity);
var negDegree = shift;
var n = Math.floor(degree); // positive degree
if (complexity == 0) {monic = true; complexity = 1}
var theCoeffMatrix = new Array();
var theOuputmatrix = new Array();
theCoeffMatrix[100] = degree;
theCoeffMatrix[101] = coeffType;
var varName = variableName;
if (variableName == 'random') varName = pickALetter();
theCoeffMatrix[102] = varName;
theCoeffMatrix[103] = negDegree;
var minInteger = 0; // this will be the lowest choice in selecting coeffs
//alert(positiveCoeffs);
// now integer coeficients
if (coeffType == 1) { 
		var theCoeff = 0;
		for (var i = n; i >= 0; i--) {
			if (numTerms < maxNumTerms) {
				var maxInteger = 4*complexity;
				if (positiveCoeffs) minInteger = 0; else minInteger = -maxInteger;
				if (i == n) theCoeff = randomNonzeroInt(minInteger, maxInteger);
				else theCoeff = randomInt(minInteger, maxInteger); 
				} // if not exceeded maxNumTerms
			else theCoeff= 0;
			theCoeffMatrix[i] = theCoeff;
			if (theCoeff!= 0) numTerms += 1;
			} // i
		} // if integer

// now rational coefficients
else if (coeffType == 2) { 
	var theCoeffN = 0, theCoeffD = 1;
	for (var i = n; i >= 0; i--) {
		if (numTerms < maxNumTerms) {
			var maxInteger = 10*complexity; 
			if (positiveCoeffs) minInteger = 0; else minInteger = -maxInteger;

			if (i == n) theCoeffN = randomNonzeroInt(minInteger, maxInteger);
			else theCoeffN = randomInt(minInteger, maxInteger);
			theCoeffN = Math.round(maxInteger*Math.random()-maxInteger/2);
			
		 	theCoeffD = randomNonzeroInt(minInteger, maxInteger);
		// must have a  non-zero coefficient to start and non-zero denominators
			} // if not exceeded maxNumTerms
		else {theCoeffN = 0; theCoeffD = 1}
		theCoeffMatrix[i] = theCoeffN/theCoeffD; 

		if (theCoeffN != 0) numTerms += 1;
		} // i
	} // if fraction 
// now decimal coefficients
else if (coeffType == 3) { 
	var theCoeff = 0; 
	var maxNumber = Math.pow(6,complexity);
	var c1 = 0, roundToThis = 0;
	for (var i = n; i >= 0; i--) {
		if (numTerms < maxNumTerms) {
			c1 = maxNumber*Math.random()-maxNumber/2;
			roundToThis = Math.round(complexity/2);
			theCoeff = roundDec(c1,roundToThis);
			while ( (i == n) && theCoeff == 0) {
				c1 = maxNumber*Math.random()-maxNumber/2;
				roundToThis = Math.round(complexity/2)+1;
				theCoeff = roundSigDig(c1,roundToThis);
				} // LEADING COEFF
			} // if not exceeded maxNumTerms
		else theCoeff = 0;
		theCoeffMatrix[i] = theCoeff;
		if (theCoeff != 0) numTerms += 1;
		} // i
	} // if decimal
// now add additional terms if less than the desired number
// these terms are just simple
if (numTerms < maxNumTerms) {
	var theAddedCoeffs = [-4, -3, -2, -1, 1, 2, 3, 4];
	for (var i = n-1; i >= 0; i--) {
		if ((numTerms < maxNumTerms) && (theCoeffMatrix[i] == 0))
			{
			var theChoice = randomInt(0,8);
			theCoeffMatrix[i] = theAddedCoeffs[theChoice];
			numTerms += 1;
			}
		}
	} // if too few terms

// fix occasional gaps in the coefficient 
for (var j = 0; j <= n; j++) {
	if (isNaN(theCoeffMatrix[j])) theCoeffMatrix[j] = 0;
	}
if(monic) theCoeffMatrix[n] = 1;
theOuputmatrix[0] = 0;
theOuputmatrix[1] = "P";
theOuputmatrix[2] = theCoeffMatrix;
return(theOuputmatrix);
	

} // createPoly



function createRational(degreeN, shiftN, degreeD, shiftD, coeffType, variableName, complexity) {
// outputs:
// [0] entry is the polynomial as a javascript expression
// [1] entry is the html in <td> tags
// [2] is an array with [2] and [3] entries the numerator 
// and denominator coefficient matrices
// the [1] component is "Q" telling us that this is a rational function
// the [0] component is the depth of the structure
// Note that the denominator degree should not be zero --
//  because such things are technically not proper rational functions
var theNumerator = createPoly(degreeN, shiftN, coeffType, variableName, complexity);
var theNArray = theNumerator[2];
// denominator must have the same variable name..
variableName = theNArray[102];
var theDenominator = createPoly(degreeD, shiftD, coeffType, variableName, complexity);


var theOutput = new Array();
theOutput[0] = 1; // a level zero structure
theOutput[1] = 'Q';
theOutput[2] = theNumerator;
theOutput[3] = theDenominator;
return(theOutput);
} // createRational


function createPower(inputExpression,exponent, exponentType) {
// Output: [0] term is the depth of the structure
// [1] term = "E" for exponent
// [2] term is the entire structure being exponentiated
// [3] is the exponent
// [4] is the exponent type: 1: integer; 2: fraction; 3: decimal
var outputArray = new Array();
outputArray[0] = inputExpression[0] + 1;
outputArray[1] = "E"; 
outputArray[2] = inputExpression;
outputArray[3] = exponent;
outputArray[4] = exponentType;
return(outputArray);
} // power

function createQuotient(numerator, denominator) {
// Output: [0] term is the depth of the structure
// [1] term = "Q" for quotient
// [2] term is the entire structure in numerator
// [3] is the structure in the denominator
// [4] 
var outputArray = new Array();
outputArray[0] = inputExpression[0] + 1;
outputArray[1] = "Q"; 
outputArray[2] = numerator;
outputArray[3] = denominator;
return(outputArray);
} // createQuotient



function createPrimitiveSum (degree, shift, parameterType, variableName, complexity, primitiveFunctions) {
var usingNonpolyPrimitives = false;
	try
		{ 
 		var k = primitiveFunctions.length;
		if (k > 0) usingNonpolyPrimitives = true;
		}
	catch (error) 
		{ 
		k = 0;
		usingNonpolyPrimitives = false;
		}

if (usingNonpolyPrimitives) {
// special primitive structures:
var thePrimitivePart = [0,"sinx",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"primitive","y",];
var theTossUp = randomInt (0, 4);
var theChoice = randomInt(0,k-1);
var theChosenPrim = primitiveFunctions[theChoice];

thePrimitivePart[1] = primitiveFunctions[theChoice];
thePrimitivePart[104] = variableName;
// alert(thePrimitivePart);
if (theTossUp == 0) {
// alert("hello" + thePrimitivePart[1]);
	return(thePrimitivePart);
	}
else if (theTossUp == 1) {
	var thePoly = createPoly(degree, shift, parameterType, variableName, complexity);
	var theResult = new Array();
	theResult[0] = 1;
	theResult[1] = "S";
	theResult[2] = thePoly;
	theResult[3] = thePrimitivePart;
// alert(theResult[3]);
	var coinToss = randomInt(0,1);
	if (coinToss == 0) theResult[4] = "+";
	else theResult[4] = "-";
	theResult[104] = variableName;
	return(theResult);
	} // else
else if (theTossUp == 3) {
	// constant multiple
	var theCoeff = randomInt(2, 4);
	var flip = randomInt(0,1);
	if (flip == 1) theCoeff *= -1;
	var theResult = new Array();
	theResult[0] = 1;
	theResult[1] = "C";
	theResult[2] = theCoeff;
	theResult[3] = thePrimitivePart;
	theResult[104] = variableName;
	return(theResult);
	} // else

else return(createPoly(degree, shift, parameterType, variableName, complexity));
} // if usingNonpolyPrimitives

else return(createPoly(degree, shift, parameterType, variableName, complexity));

} // createPrimitiveSum


// the next function should go -- 
// however it is still used in the game for the difference quotient...
function createAndFormatRational(degreeN, degreeD, coeffType, variableName, complexityNumerator, complexityDenominator) {
// outputs:
// [0] entry is the polynomial as a javascript expression
// [1] entry is the html in <td> tags
// [2] is an array with [2] and [3] entries the numerator 
// and denominator coefficient matrices
// the [1] component is "F" telling us that this is a fraction
// the [0] component is the depth of the structure
// Note that the denominator degree should not be zero --
//  because such things are technically not proper rational functions
var theNumerator = createPoly(degreeN, 0, coeffType, variableName, complexityNumerator);
var theNArray = theNumerator[2];
// denominator must have the same variable name..
variableName = theNArray[102];
var theDenominator = createPoly(degreeD, 0, coeffType, variableName, complexityDenominator);
var theDArray = theDenominator[2];
var theOutputN = formatPolyTex (theNumerator);
var theOutputD = formatPolyTex (theDenominator);
var theOutput = new Array();
theOutput[0] = '(' + theOutputN[0] + ')/(' +  theOutputD[0] + ')';
theOutput[1] = '\\frac{' + theOutputN[1] + '}{' + theOutputD[1] + '}';
// alert(theOutput[1]);
theOutput[2] = new Array();
theOutput[2][0] = 0; // depth 0 here
theOutput[2][1] = "F";
theOutput[2][2] = theNArray;
theOutput[2][3] = theDArray;
return(theOutput);
} // createandFormatRational


// ****************** formating routines
function formatLinear(m,b,varName, coeffType,rounding) {
// coeffType 1: integer; 2: fraction; 3: decimal
// returns array[JSequation, TeXeqn];
// Create a poly object
if (coeffType != 2) var thePoly = [0,"P",[roundDec(b,rounding), roundDec(m,rounding)]];
else var thePoly = [0,"P",[b, m]];
thePoly[2][100] = 1;
thePoly[2][101] = coeffType;
thePoly[2][102] = varName;
thePoly[2][103] = 0;
//alert(formatPolyTex(thePoly,true,true,false,false));
return(formatPolyTex(thePoly,true,true,false,false) );
// see below
} // formatLinear

function formatPoly(coeffArray,varName, coeffType,rounding) {
// coeffType 1: integer; 2: fraction; 3: decimal
// returns array[JSequation, TeXeqn];
// Create a poly object
var theDegree = coeffArray.length-1;
if (coeffType != 2) {
	for (var i = 0; i <= theDegree; i++) coeffArray[i] = roundDec(coeffArray[i],rounding);
	var thePoly = [0,"P", coeffArray];
	}
else var thePoly = [0,"P", coeffArray];
thePoly[2][100] = theDegree;
thePoly[2][101] = coeffType;
thePoly[2][102] = varName;
thePoly[2][103] = 0;
//alert(formatPolyTex(thePoly,true,true,false,false));
if (coeffType == 2) return(formatPolyTex(thePoly,true,true,false,false) );
else return(formatPolyTex(thePoly,true,false,false,false) );
// see below
} // formatPoly


function formatPolyTex(theInputmatrix, negExponents,rationalExponents,asRadicals,putTags) {
// TODO as radicals is not yet done
// if negExponents is true then perits them else puts them in denominator when the coefficients are rational or integer
// if putTags is true it puts <span = math> tags around it.
// alert("hello");
var inTags = true;
try {inTags = putTags}
catch(error) {}
if (inTags != false) inTags = true; // in case undefined

var theCoeffMatrix = theInputmatrix[2];
// alert(theInputmatrix[2]);
// coeffType 1: integer; 2: fraction; 3: decimal
// takes a matrix of coefficients and returns an array
// [0] entry is the polynomial as a javascript expression
// [1] entry is the html in <td> tags
// [2] is the coefficient matrix that was input

var degree = theCoeffMatrix[100];
var coeffType = theCoeffMatrix[101];
var variableName = theCoeffMatrix[102];
var negDegree = theCoeffMatrix[103];
var LogPos = theCoeffMatrix[99]; // only one log permitted

var theJscriptString = '', theTexString = '';
var theOutput = ['',''];
var theCoeff = '';
var theTdCoeffArray = new Array();
var n = degree;
var varName = variableName;
var exponStr = ''; // will be exponent string
var absExponStr = ''; // abs value of above
var exponJStr = ''; 
	// will be exponent string for Javascript (parens around exponents)
var absExponJStr = ''; // abs value of above

if (!(negExponents)&&(coeffType == 1)) coeffType = 2; 

// now integer coefficients
if ((coeffType == 1) || (coeffType == 3)) { 
	if (inTags) theTexString += '<span class = math>';
	for (var i = n; i >= 0; i--) {
		var I = i - negDegree; // I = actual exponent 
		if (rationalExponents) {
			var theFrac = toFracArr(I);
			exponStr = toFracArr(I)[3];
			absExponStr = toFracArr(Math.abs(I))[3];
			if (theFrac [4] == 0) { 
				exponJStr = exponStr;
				absExponJStr = absExponStr
				}
			else {
				exponJStr = "(" + exponStr + ")";
				absExponJStr = "(" + absExponStr + ")"
				}
			} // rationalExponents
		else {
			exponStr = roundSigDig(I,5);
			absExponStr = roundSigDig(Math.abs(I),5);
			exponJStr = exponStr;
			absExponJStr = absExponStr;
			}

		theCoeff  = parseFloat(theCoeffMatrix[Math.floor(i)]);
		if ((theCoeff > 0)&&(theJscriptString != '')) {
			theTexString += ' + ';
			theJscriptString += ' + ';
			}
		else if (theCoeff < 0) {
			if (theJscriptString == '') theTexString += '-';
			else theTexString += ' - ';
			if (theJscriptString == '') theJscriptString += '-';
			else theJscriptString += ' - ';
			theCoeff = - theCoeff;
			}
		
		
		if (theCoeff != 0) {
			if ((Math.abs(theCoeff) != 1)||(I == 0)) {
				theTexString += roundDec(theCoeff,4);
				theJscriptString += roundDec(theCoeff,4);
				}
			if (I == 1) {
				theTexString += varName;
				if (theCoeff == 1) theJscriptString += 'x';
				else theJscriptString += 'x';
				} // degree = 1
			else if (I != 0) {
				theTexString +=  varName  + '^{' + exponStr + '}';
				if (theCoeff == 1) theJscriptString += 'x^' + exponJStr;
				else theJscriptString += 'x^' + exponJStr;
				} // degree > 0
			// now add the log term if necessary

				if ((I == 0) && !(LogPos == undefined)) {
					theJscriptString += "ln(abs(x))";
					theTexString += "\\ln |x|";

				}
			} // if nonzero coeff
		
		} // i
	if (inTags) theTexString += '</span>'
	} // if integer

// now rational coefficients
else if (coeffType == 2) { 
	if (inTags) theTexString += '<span class = math>';
	var theCoeffN = 0, theCoeffD = 1;
	for (var i = n; i >=0; i--) {
		var I = i - negDegree; // I = actual exponent
		if (rationalExponents) {
			var theFrac = toFracArr(I);
			exponStr = toFracArr(I)[3];
			absExponStr = toFracArr(Math.abs(I))[3];
			if (theFrac [4] == 0) { 
				exponJStr = exponStr;
				absExponJStr = absExponStr
				}
			else {
				exponJStr = "(" + exponStr + ")";
				absExponJStr = "(" + absExponStr + ")"
				}
			} // rationalExponents
		else {
			exponStr = roundSigDig(I,5);
			absExponStr = roundSigDig(Math.abs(I),5);
			exponJStr = exponStr;
			absExponJStr = absExponStr;
			}

		theCoeff  = parseFloat(theCoeffMatrix[Math.floor(i)]);
// alert("degree =" +i + " coeff =" + theCoeff);
		var theFrac = toFracArr(theCoeff);
		theCoeffN = theFrac[1]; theCoeffD = theFrac[2];
		if ((theCoeffN > 0)&&(theJscriptString != '')) {
			theTexString += ' + ';
			theJscriptString += ' + ';
			}
		else if (theCoeffN < 0) {
			if (theJscriptString == '') theTexString += '-';
			else theTexString += ' - ';
			if (theJscriptString == '') theJscriptString += '-';
			else theJscriptString += ' - ';
			theCoeffN = - theCoeffN;
			}
		if (theCoeffN != 0) {

			if ((I < 0) && !(negExponents)) {
				if (theCoeffD == 1) {
					if  (!(I == -1)) {
						theTexString += '\\frac{' + theCoeffN + '}{' + varName + '^{'+ absExponStr + '}}';
						theJscriptString += theCoeffN + '/x^'+ absExponJStr;
						}
					else {
						theTexString += '\\frac{' + theCoeffN + '}{' + varName + '}';
						theJscriptString += theCoeffN + '/x';
						}
					}
				else {
					if  (!(I == -1)) {
						theTexString += '\\frac{' + theCoeffN + '}{' + theCoeffD + varName + '^{'+ absExponStr + '}}';
						theJscriptString += theCoeffN + '/(' + theCoeffD + 'x^'+ absExponJStr + ')';
								}
					else {theTexString += '\\frac{' + theCoeffN + '}{' + theCoeffD +  varName + '}';
					theJscriptString += theCoeffN + '/(' + theCoeffD + 'x)';
					}
					}

			} 
			else if ((theCoeffD != 1)&&(theCoeffN != 1)) {
				if ( !(I == 1) && !(I == 0)) {
					theTexString += '\\frac{' + theCoeffN + varName + '^{'+ exponStr + '}}{' + theCoeffD + '}';
					theJscriptString += theCoeffN + 'x^'+ exponJStr +'/' + theCoeffD;
					}
				else if (I == 1) {
					theTexString += '\\frac{' +  theCoeffN + varName + '}{' + theCoeffD + '}';
					theJscriptString += theCoeffN + 'x/' + theCoeffD;
					} // degree = 1
				else if (I == 0) {
					theTexString += '\\frac{' +  theCoeffN + '}{' + theCoeffD + '}';
				theJscriptString += theCoeffN + '/' + theCoeffD;
				} // degree = 0
			} // if none-1 coeff
		else if ((theCoeffD != 1)&&(theCoeffN == 1)) {
				if ( !(I == 1) && !(I == 0)) {
					theTexString += '\\frac{' + varName + '^{'+ exponStr + '}}{' + theCoeffD + '}';
					theJscriptString += 'x^'+ exponJStr +'/' + theCoeffD;
					}
				else if (I == 1) {
					theTexString += '\\frac{' + varName +  '}{' + theCoeffD + '}';
					theJscriptString += 'x/' + theCoeffD;
					} // degree = 1
				else if (I == 0) {
					theTexString += '\\frac{1}{' + theCoeffD + '}';
				theJscriptString += '1/' + theCoeffD;
				} // degree = 0
			} // if numerator is 1
		else if ((theCoeffD == 1)&&(theCoeffN != 1)) {
				if ( !(I == 1) && !(I == 0)) {
					theTexString += theCoeffN + varName + '^{'+ exponStr + '}';
					theJscriptString += theCoeffN + 'x^'+ exponJStr;
					}
				else if (I == 1) {
					theTexString += theCoeffN +  varName;
					theJscriptString += theCoeffN + 'x';
					} // degree = 1
				else if (I == 0) {
					theTexString += theCoeffN;
				theJscriptString += theCoeffN;
				} // degree = 0
			} // if only denom is 1
		else if ((theCoeffD == 1)&&(theCoeffN == 1)) {
				if ( !(I == 1) && !(I == 0)) {
					theTexString += varName + '^{'+ exponStr + '}';
					theJscriptString += 'x^'+ exponJStr;
					}
				else if (I == 1) {
					theTexString += varName;
					theJscriptString += 'x';
					} // degree = 1
				else if (I == 0) {
					theTexString += '1';
				theJscriptString += 1;
				} // degree = 0
			} // if only denom is 1
					// now add the log term if necessary

				if ((I == 0) && !(LogPos == undefined)) {
					theJscriptString += "ln(abs(x))";
					theTexString += "\\ln |x|";

				}
		} // if non-zero fraction
	
		
		} // i
	if (inTags) theTexString += '</span>'
	} // if fraction 

if (theJscriptString == '') {theJscriptString = "0"; theTexString = "0"}
if (asRadicals) theTexString = convertToRadicals(theTexString);
theOutput[0] = theJscriptString;
theOutput[1] = theTexString;
theOutput[2] = theCoeffMatrix;

return(theOutput);
	
} // formatPoly

function polyDeriv(theInMatrix,order) {
// **** Accuracy Warning
// In decimal mode this rounds derivative coeffs to 10 decimal places
// and antiderivative coeffs to 5 places as defined in the following 
// two constants. (The deriv rounding is just to prevent suprious
// Jscript computation chopping errors.)
var theInputmatrix = theInMatrix.slice(0,103);
var theCoeffMatrix = theInputmatrix[2];
var derivAcc = 10;
var antiderivAcc = 5;
var negDegree = theCoeffMatrix[103];
var theDeriv = theInputmatrix;
if (order == 0) return(theDeriv);
else if (order > 0) {
	var theDerivCoeffs = new Array();
	theDerivCoeffs[100] = theCoeffMatrix[100];
	theDerivCoeffs[101] = theCoeffMatrix[101]; // coefftype
	theDerivCoeffs[102] = theCoeffMatrix[102]; // varname
	theDerivCoeffs[103] = theCoeffMatrix[103] + 1; 
// varname
	var deg = theCoeffMatrix[100];
	var n = Math.floor(deg);
	var decPart = deg - n;
//if(theTest) alert(order + '   ' + n);
	for (var i = 0; i <= n; i++) {
		if (theDerivCoeffs[101] != 3) theDerivCoeffs[i] = (i + decPart - negDegree)*theCoeffMatrix[i];
		else theDerivCoeffs[i] = roundDec((i + decPart - negDegree)*theCoeffMatrix[i], derivAcc);
		} // i
	theDeriv[2] = theDerivCoeffs;
	return(polyDeriv(theDeriv,order-1));
	} // if positive order (derivative)
else {
	var theAderiv = theInputmatrix;
	var theAderivCoeffs = new Array();
	theAderivCoeffs[100] = theCoeffMatrix[100] + 1;
	if (theCoeffMatrix[101] == 1) theAderivCoeffs[101] = 2;
	else theAderivCoeffs[101] = theCoeffMatrix[101];
	theAderivCoeffs[102] = theCoeffMatrix[102];
	theAderivCoeffs[103] = theCoeffMatrix[103];
	var n = theAderivCoeffs[100];
	theAderivCoeffs[0] = 0;
	for (var i = 1; i <= n; i++) {
		if (i == theAderivCoeffs[103]) {
			theAderivCoeffs[i] = theCoeffMatrix[i-1];
			theAderivCoeffs [99] = i; // indicates a ln|x|
			}
		else {
			if (theAderivCoeffs[101] != 3) theAderivCoeffs[i] = theCoeffMatrix[i-1]/(i- theAderivCoeffs[103]);
			else theAderivCoeffs[i] = roundDec(theCoeffMatrix[i-1]/(i- theAderivCoeffs[103]),antiderivAcc);
			} 
		} // i
// if(theTest) alert(order+1 + '   ' + n);
// theTest = false;
	theAderiv[2] = theAderivCoeffs;
// alert(polyDeriv(theAderiv,order+1)[2][99])
	return(polyDeriv(theAderiv,order+1));
	} // if negative order (antiderivative)
}

// * Javascript loaders **
// * These are due to
// http://www.phpied.com/javascript-include/
function include(script_filename) {
    document.write('<' + 'script');
    document.write(' language="javascript"');
    document.write(' type="text/javascript"');
    document.write(' src="' + script_filename + '">');
    document.write('</' + 'script' + '>');
}
// writedoc loader

function include_dom(script_filename) {
    var html_doc = document.getElementsByTagName('head').item(0);
    var js = document.createElement('script');
    js.setAttribute('language', 'javascript');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', script_filename);
    html_doc.appendChild(js);
    return false;
}

// * Now one that includes new JS only if already included

var included_files = new Array();

function include_once(script_filename) {
    if (!in_array(script_filename, included_files)) {
        included_files[included_files.length] = script_filename;
        include_dom(script_filename);
    }
}

function in_array(needle, haystack) {
    for (var i = 0; i < haystack.length; i++) {
        if (haystack[i] == needle) {
            return true;
        }
    }
    return false;

}

// * end of on-the-fly javascript includers

// ** the length of a string in pixels

function visualLength(inStr) {
	var ruler=document.getElementById("ruler");
	ruler.innerHTML = inStr;
//alert(ruler. offsetWidth)
	return (ruler.offsetWidth);
}


// *** alerts with accents
function myAlert(inString) {
var y=document.createElement('span');
y.innerHTML = inString;
alert(y.innerHTML);
} // myAlert()

function riemannSum(func,a,b,n,type) {
// type: 1 = left, 2 = right, 3 = trap
// x must be global
// n is not used for random Riemann sums
x = a;
var dX = (b - a)/n;
if ((type >= 1)&& (type < 4)) {
	var lsum = 0;
	var rsum = 0;
	var y  = eval(func);
	if (isBad(y)) { return(false) }
		
	for (var k = 1; k<= n; k++)
		{
		lsum += y* dX;
		x = a + k* dX;
		y  = eval(func);
		if (isBad (y)) { };
		rsum += y* dX;
		}
	if (type == 1) return(lsum);
	else if (type == 2) return(rsum);
	else if (type == 3) return((lsum+rsum)/2);
	} // non-random

}

function simpsonSum(func,a,b,n) {

// type: 1 = left, 2 = right, 3 = trap
// x must be global
// n is not used for random Riemann sums
var deltax = (b - a)/n;
x = a;
var sum = 0;
for (var k = 1; k<= n-1; k +=2) {
	y  = eval(func);
	if (isBad(y)) { return(false) }
	sum += y*deltax;
	x = a + k*deltax;
	y  = eval(func);
	if (isBad(y)) { return(false) }
	sum += 4*y*deltax;
	x = a + (k+1)*deltax;
	y  = eval(func);
	if (isBad(y)) { return(false) }
	sum += y*deltax;
	}
sum = sum/3;
return(sum)
} // simpsonSum

// *********Start of adaptive Quadrature Routines
function AdaptiveQuadrature(func, a, b, tolerance) {
var errorBank = 0;
tolerance = tolerance/2;
var excusedCount = 0;
// will permit errors to accumulate to tol/2 if deltaX < (b-a)/100

function S(aa, bb) {
// part of adaptive quadrature
var m = (bb+aa)/2;
x = aa;
var fa = eval(func);
x = bb;
var fb = eval(func);
x =m;
var fm = eval(func); 

return (bb-aa)*(fa+4*fm+fb)/6;


} // function S


function approx(c, d, delta) {
// part of adaptive Quadrature
// this is recursive so watch out!!!
	var sum = 0;
	var m = (c+d)/2;
	var error1 =  Math.abs( S(c,d) - S(c, m) - S(m, d)) /15;
	var errorScaled = error1*(b-a)/(d-c);
// alert ("error = "+ errorScaled + "  Tolerance = " + delta +  " Am I done = " + (error1 <= delta));
	if (errorScaled <= delta) {
		errorBank += error1; // have some to spare
//alert((delta-error1)*(d-c)/(b-a));
		sum = S(c, m)+ S(m, d);
		}
	else 
		{
		var excused = false
		if (d-c < (b-a)/100) {
			if (error1 + errorBank < tolerance/2 ) {
				errorBank += error1;
				sum = S(c, m)+ S(m, d);
				excused = true;
				excusedCount += 1;
				}
			
			}
		if ((!excused) && (recursionCount >= maxRecursionCount))
			{
			tooDeep = true; 
			sum = S(c, m)+ S(m, d);
			} // if stack depth exceeded
		else if (!excused)
			{
			sum = approx(c, m,delta) + approx(m, d,delta);
			recursionCount+=1
			}
		}

	return(sum);	// returns S(a,b) if small enough, else the sum of smaller pieces if not

} // approx


// tol is the error 
var recursionCount = 0;
var tooDeep = false;
var maxRecursionCount = 10000;
	 // func is already parsed can now just evaluate it as usual
var qq = (b-a)/3;
var theValue = approx(a,a+qq,tolerance/3) + approx(a+qq,a+2*qq,tolerance/3) + approx(a+2*qq,b,tolerance/3);

if (tooDeep) myAlert("Exceeded limit of " + maxRecursionCount + " subdivisions. Answer is not reliable. <br>Decrease the desired accuracy and try again.")
//alert("EcxcusedCount = " + excusedCount);
return theValue;
} // AdaptiveQuadrature



// *********End of adaptive Quadrature Routines

function quadraticCurve(x1, y1, x2, y2, x3, y3) {
// out goes a quadratic function of x
// uses Lagrange interpolation
var theDenoms = [(x1-x2)*(x1-x3), (x2-x1)*(x2-x3), (x3-x1)*(x3-x2)];
// numerators y1(x-x2)(x-x3), y2... etc
var theNumers = [y1.toString() + "*(x-" + x2.toString() + ")*(x-" + x3.toString() + ")", y2.toString() + "*(x-" + x1.toString() + ")*(x-" + x3.toString() + ")", y3.toString() + "*(x-" + x1.toString() + ")*(x-" + x2.toString() + ")"];
var theOut = theNumers[0] + "/" + theDenoms[0].toString() + " + " + theNumers[1] + "/" + theDenoms[1].toString()+ " + " +  theNumers[2] + "/" + theDenoms[2].toString();
theOut = replaceSubstring(theOut,"--","+");
//alert(theOut)
return(theOut);
} 


