// ***************** COPYRIGHT (c) 2006 STEFAN WANER ******************
// *********************** ALL RIGHTS RESERVED ************************



// ***  IT ALSO REMAINS TO DO THE TOPMATTER


// Globals
window.onerror = myErrorTrap;
var e = 2.718281828459045;
var pi = 3.141592653589793;
var theColor = 0; // the color of a pixel
var numColors = 7;
var theDot = new Array();
var counter = 0; // for debugging
	theDot[0] = new Image();
	theDot[0].src = "pixelwhite.gif"
	theDot[1] = new Image();
	theDot[1].src = "pixelblack.gif"; 
	theDot[2] = new Image();
	theDot[2].src = "pixelred.gif"; 
	theDot[3] = new Image();
	theDot[3].src = "pixelgreen.gif";
	theDot[4] = new Image();
	theDot[4].src = "pixelblue.gif";
	theDot[5] = new Image();
	theDot[5].src = "pixelmagenta.gif";
	theDot[6] = new Image();
	theDot[6].src = "pixelorange.gif";
	theDot[7] = new Image();
	theDot[7].src = "pixelyellow.gif";
	theDot[8] = new Image();
	theDot[8].src = "litepixelblack.gif"; 
	theDot[9] = new Image();
	theDot[9].src = "litepixelred.gif"; 
	theDot[10] = new Image();
	theDot[10].src = "litepixelgreen.gif";
	theDot[11] = new Image();
	theDot[11].src = "litepixelblue.gif";
	theDot[12] = new Image();
	theDot[12].src = "litepixelmagenta.gif";
	theDot[13] = new Image();
	theDot[13].src = "litepixelorange.gif";
	theDot[14] = new Image();
	theDot[14].src = "litepixelyellow.gif";
var X = 0;
var x = 0;
var y = 0;
var x1 = 0;
var x2 = 0;
var y1 = 0;
var y2 = 0;
var h = 0;
var xh = 0;
var hx = 0;
var t = 0;
var th = 0;
var ht = 0;
var a = 0;
var b = 0; // end points of graph
var c = 0;
var d = 0;
var A = 0;
var p1 = 0;
var numDivisions = 50; // for each curve
var quoteMark = unescape( '%22' );
var singlequoteMark = unescape( '%27' );

var numX = 200; // picure width in pixels
var numY = 200; // picture height
var infinity = 10000000000000  // 10^13;
var windowcropTally = 20; 
	// will not cut a window in half if more than this number of 
     // pixels pop out of range as a result


var theCanvas = new makeArray2(numX,numY);

var autoY = true;
var autoGridline = true;

var okToRoll = true;

var theString = "";

var theFunction = ""; // the function


var xGridLines = new Array();
var yGridLines = new Array();
var lineColor = 2; // red
var lineColorLite = lineColor + 7;

var xAxisPosition = 50; // position of axes = -1 if not visible
var yAxisPosition = 100;
var xGridStep = 10;
var yGridStep = 10;

var xVals = new Array(); // to store the values of x for evlauator
var yVals = new Array();  // to store the functions
var tminVals = new Array(); 
var tmaxVals = new Array();

var arraysize = 0; // number of functions
var xArraysize = 0; // number of x-values in evalautor
var maxnum = 5;  // max number of functions allowed
var maxnumX = 12; // max number of x-values allowed
var theValues = new makeArray2(maxnumX, maxnum); // copy of evalauator data

var fracMode = false; // fraction mode off default
var numSigDigs = 5;  // rounding of y-values default
var maxDenom = 99999;

// *** end globals


// *** Error Handler ******
function myErrorTrap(message,url,linenumber) {
alert("It looks like you have entered something wrongly. Press 'Show Examples' to see to to format functions.");
return (true);
} // end of on error

// ********************

// **********Utilities to read in the functions *********
// ********************************************************************
function setUp(){
	okToRoll = true;
	with(Math){
		
		//Step 1: read the inputs into an array and decide
		// how many points there are
		var thexCellName = ""; // these are strings to names x1, x2,.. 

		var theInstruction = ""; // a javascript instuction
		var doIt = 0; // a dummy variable
				
		//Step 2: Fill the y arrays with the functions 
		for (var i = 1; i <= maxnum; i++) {
			theInstruction = "yVals["+i+"] = stripSpaces(document.theFormA.y"+i+".value);"
			doIt = eval(theInstruction);

			} // i
		// Step 3: Compute how many functions there are
		for (i = 1; i <= maxnum; i++) {
			if (yVals[i] == "") {
				arraysize = i-1;
				i = maxnum}
			} // i
		if (arraysize == 0) {alert("You must enter at least one function"); okToRoll = false}

	} //with math
} // setup

// ***********************************************************************
// ************** End of Reading-in Parametric Equations Utility ******
// ***********************************************************************


// ***********************************************************************
// ************** Evaluator Setup Routine ******
// ***********************************************************************
function setUp2() {
	okToRoll = true;
	var a = setUp(); // read in the functions
	fracMode = document.theFormC.fracModeButton.checked;
	if(!fracMode) {
		numSigDigs = document.theFormC.acc.value;
		if (isBad(numSigDigs)) {alert("the Number of Significant Digits must be a number. I am setting it back to 5. Change it to another number if you like."); okToRoll = false}
	} // if not fraction mode
	// Now read in the x-values
	with(Math){
		//Step 1: read the inputs into an array and decide
		// how many points there are
		var thexCellName = ""; // these are strings to names x1, x2,.. 

		var theInstruction = ""; // a javascript instuction
		var doIt = 0; // a dummy variable
				
		//Step 2: Fill the x array with the x-values
		// Note: at this point theywill all be strings 
		for (var i = 1; i <= maxnumX; i++) {
			theInstruction = "xVals["+i+"] = stripSpaces(document.theFormC.x"+i+".value);"
			doIt = eval(theInstruction);
			} // i
		// Step 3: Compute how many functions there are
		for (i = 1; i <= maxnumX; i++) {
			if (xVals[i] == "") {
				xArraysize = i-1;
				break
				} // encountered blank
			} // i
		if (xArraysize == 0) {alert("You must enter at least one value of x"); okToRoll = false}
// alert(xArraysize);
	} //with Math
} // end of function setUp2
// ***********************************************************************
// ************** End of Evaluator Setup Routine ******
// ***********************************************************************


// ***********************************************************************
// ************** Evaluate all Routine ******
// ***********************************************************************

function evaluateAll() {
// This evaluates all the functions in nthe table
// we have arraysize many functions and xArraysize many values of x 

var theStrg = ""; // a dummy string for parsed functions
var theInstruction = ""; // a javascript instuction
var doIt = 0; // a dummy action

// First clear the current y-values
for (var i = 1; i <= maxnum; i++) {
	for (var j = 1; j <= maxnumX; j++) {
		theInstruction = "document.theFormC.y"+i+"x"+j+".value = ''";
		doIt = eval(theInstruction)
		} // j
	} // i

// Now Compute the values
 
for (var j = 1; j <= arraysize; j++) {
	theStrg = myParse(yVals[j]);
	for (var i = 1; i <= xArraysize; i++) {
		x = myEval(xVals[i]); // in case there are inputs like "sin(3)"
		y = checkEval(theStrg);
		if (y > infinity) y = "infinity";
		else if (y < -infinity) y = "-infinity";
		if (!fracMode) theInstruction = "document.theFormC.y"+j+"x"+i+".value = roundSigDig(y,numSigDigs)";
		else theInstruction = "document.theFormC.y"+j+"x"+i+".value = toFrac(y, maxDenom)";
		doIt = eval(theInstruction);
// alert(theInstruction);
		} // end i
	} // end j
} // end of evaluateAll

// ***********************************************************************
// ************** End of Evaluate all Routine ******
// ***********************************************************************

// **************** Plot All Curves ********************
function plotAllCurves(){
var theyString = "";
var thexMin = 0;
var thexMax = 0;
lineColor = 2; // red
for (var i = 1; i <= arraysize; i++) {
	
	theyString = yVals[i];
	thexMin = a;
	thexMax = b;
	var p = drawCurve(theyString, thexMin, thexMax);
	lineColor +=1;
	if (lineColor == 8) lineColor = 2;   // no yellow please
	} // i
} // end of plot

// ******************End of Plot All Curves ***********
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();
    }


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 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 makeArray2 (X,Y)
	{
	var count;
	this.length = X+1;
	for (var count = 1; count <= X+1; count++)
		// to allow starting at 1
		this[count] = new makeArray(Y);
	} // makeArray2

function makeArray (Y)
	{
	var count;
	this.length = Y+1;
	for (var count = 1; count <= Y+1; count++)
		this[count] = 0;
	} // makeArray


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

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



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 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 (isBad(theNumber)) return theNumber;
		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 PARSER ***********
function myEval(theString)
{
if (isBad(x)) return x;
return(eval(myParse(theString)));
	
}

function checkEval(theString)
{
if (isBad(x)) return x;
return(eval(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 two pieces
	// *** 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 ( ( (TempChar == "*") || (TempChar == "/") || (TempChar == "-")  ) || ( (TempChar == "+") || (TempChar == ')' )  )  )
				{ 
				
					{
					// alert(Count);
					looking = false;
					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 (InString, RefString)  {
	if(InString.length!=1) 
		return (false);
	if (RefString.indexOf (InString, 0)==-1) 
		return (false);
	return (true);
}

function putProduct(InString) {
OutString="";
for (Count=0; Count < InString.length; Count++)  {
		TempChar=InString.substring (Count, Count+1);
		if (!isCharHere(TempChar,"xXeslcapdDuUhHtT(") || (Count == 0) )
			{OutString=OutString+TempChar}
		else 
			{
			if (isNumberChar(InString.substring(Count-1,Count)))
				{OutString=OutString+"*"+TempChar}
			else OutString=OutString+TempChar
			}
	}
	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]);
				outString = leftRest+"Math.pow("+Arg1+","+Arg2+")"+rightRest;
				
				} // end hif 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 isBad(x) {
if((isNaN(x)) || (x > infinity) || (x < -infinity)) return(true);
else return(false)
}

function myParse(expression)
{
		var theString = stripSpaces(expression);		
		with (Math)
			{
		// now convert formatting from GC formatting		
		theString = putProduct(theString);
		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
		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,"sqrt","Math.sqrt");
		return(theString);
} // myParse
// ******** END FUNCTION PARSER **************

function toFrac(x, maxDenom) {
	if (isBad(x)) return x;
	var theFrac = new Array();
	theFrac[1] = 0;
	theFrac[2] = 0;
	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;

	if (theFrac[2] == 1) return (theFrac[1].toString());
	else return (theFrac[1] + "/" + theFrac[2]);

} // toFrac




// ***********************************************
// **** Graphing Routine
// ***********************************************
function readBasics() {
// alert("here");
var theStrg = "";  // dummy string for evaluating functions
autoY = document.theFormB.autoYButton.checked;
autoGridline = document.theFormB.autoGridButton.checked;
// get the graph window information
	for (var k = 1; k <= 1; k++)
	{
	var aa = document.theFormB.a.value; 
	if (aa == "") { alert("You have not entered a number for xMin."); okToRoll = false; break;}
	a = myEval(aa);
	if (isNaN(a) ) { alert("You have not entered a number for xMin."); okToRoll = false; break;}
	var bb = document.theFormB.b.value; 
	if (bb == "") { alert("You have not entered a number for xMax."); okToRoll = false; break}
	b = myEval(bb); 
	if (isNaN(b) ) { alert("You have not entered a number for xMax."); okToRoll = false; break;}
	if ( (okToRoll) && (a >= b)) { alert("xMax should be larger than xMin"); okToRoll = false; break;}
	if(!autoY) {
// alert("Here autoY is off");
		var cc = document.theFormB.c.value; 
		if (cc == "") { alert("You have not entered a number for yMin. (Otherwise click on 'Auto'.)"); okToRoll = false; break}
		c = myEval(cc); 
		if (isNaN(c) ) { alert("You have not entered a number for yMin. (Otherwise click on 'Auto'.)"); okToRoll = false; break;}
		var dd = document.theFormB.d.value; 
		if (dd == "") { alert("You have not entered a number for yMax. (Otherwise click on 'Auto'.)"); okToRoll = false; break}
		d = myEval(dd); 
		if (isNaN(d) ) { alert("You have not entered a number for yMax. (Otherwise click on 'Auto'.)"); okToRoll = false; break;}
		if ( (okToRoll) && (c >= d)) { alert("yMax should be larger than yMin"); okToRoll = false; break;}
		} // end of if autoY
	else {
		document.theFormB.c.value = ''; // cannnot have it both ways
		document.theFormB.d.value = ''; 
		// Must compute ymin and ymax here
		var deltax = (b-a)/numX;
		var maxy = 0;
		var miny = 0;
		var firstCheck = true;
		for (var j = 1; j <= arraysize; j++) {
			theStrg = myParse(yVals[j]);
			for (var i = 0; i <= numX; i++)
				{
				x = a + i*deltax;
// alert("x = " + x);
				y  = checkEval(theStrg);
				if (!isNaN(y) &&( y < infinity) && (y > -infinity))
					{
					// alert ("y = " + y);
					if (firstCheck) 
						{
						firstCheck = false; 
						maxy = y; 
						miny = y
						}
					if (y > maxy) maxy = y;
					else if (y < miny) miny = y;
					} // end of if y is a legit number
				} // i
			} // j
	
// alert("miny = "+miny + "maxy =" + maxy);
			// Now cut down the size of the window if necessary
			// in the case of graphs shooting off to infinity
			// the tequnique is to eliminate "outliers" by cropping 
			// the window.


// *** Why don't we just compute the st deviation and crop to +- 3s
			var cutting_down = true;
			var invisible_tally = 0;
			while (cutting_down)
				{
				maxy = maxy/2;
				miny = miny/2;
				invisible_tally = 0;
				for (var j = 1; j <= arraysize; j++) {
					theStrg = myParse(yVals[j]);

					for (var i = 0; i <= numX; i++)
						{
						x = a + i*deltax;
						y  = checkEval(theStrg);
						if (!isNaN(y) &&( y < infinity) && (y > -infinity))
							{
							if ((y > maxy) || (y < miny))
								{
								invisible_tally++;
								if (invisible_tally > windowcropTally)
									{
									cutting_down = false;
									maxy = 2*maxy;
									miny = 2*miny;
									i = numX;
									break;
									} // too many invisible;
								} // if y > ymax
						} // if is a number
					} // i
				} // j
			} // while cutting_down
// alert("miny = "+miny + "maxy =" + maxy);
		
		if (miny == maxy) {miny  = miny-1; maxy += 1}
		var scalefactor = 150/(maxy - miny); 
// ************
// end of y min and ymax window coords
// ************

		c = eval(roundSigDig(miny,4)); // sets the globals
		d = eval(roundSigDig(maxy,4));
		} // end of else for autoY
// alert("c = " + c + " d = " + d);
// At this point we have the globals a, b, c, d we need
	if(!autoGridline) {
		var xk = document.theFormB.xg.value;
		if (xk == "") xGridStep = 0;
		else xGridStep = eval(xk);
		var yk = document.theFormB.yg.value;
		if (yk == "") yGridStep = 0;
		else yGridStep = eval(yk);
		} // end of if not autoGridline
	else {
		document.theFormB.xg.value = ''; 
		document.theFormB.yg.value = ''; // cannnot have it both ways.
		var pq = Math.round((b-a)/8);
		if (pq == 0) pq = (b-a)/8;
		xGridStep = pq;
		pq = Math.round((d-c)/8);
		if (pq == 0) pq = (d-c)/8;
		yGridStep = pq;
		} // end of else for autoGridline
	} // end of single loop (k)
}

// ********************************************************************
// *********** Initilaize the Canvas & Draw the Axes and Gridlines ****
// ********************************************************************

function makeGraph()
{
// first initialize the canvas
for (var i = 1; i <= numX; i++) for (var j = 1; j <= numY; j++) theCanvas[i][j] = 0;
// first read and parse the function

	readBasics();

if (okToRoll)
	{
// alert("a = "+a+"b="+b+"c="+c+"d = "+d);
	// now draw the axes & gridlines
	// gridlines first
	// setup position of gridlines
	if (xGridStep > 0) xGridLines[0] = Math.floor(  (b-a)/xGridStep );
	else xGridLines[0] = 0;
	if (yGridStep > 0) yGridLines[0] = Math.floor(  (d-c)/yGridStep );
	else yGridLines[0] = 0;
	// initialize these arrays to zero 
	for (var i = 1; i <= xGridLines[0]; i++) xGridLines[i] = 0;
	for (var i = 1; i <= yGridLines[0]; i++) yGridLines[i] = 0;
	var pq = 0; // just a dummy variable
	if (a*b >= 0) {
		for (var i = 1; i <= xGridLines[0]; i++)
			{
			xGridLines[i] = Math.round( i*xGridStep*numX/(b-a)  ) 
// alert("i = " + i + " : " + xGridLines[i] + cr);
			} // x Grid Line Positions
		} // if a and b have the same sign
	else 
		{ // want the y-axis to be one of the gridlines
		for (var i = 1; i <= xGridLines[0]; i++)
			{
			pq = Math.round((i*xGridStep - a)*numX/(b-a));
			if ((pq <= numX) && (pq >= 0)) xGridLines[i] = pq;
			else break;
			} // loop i
		for (var j = 1; j <= xGridLines[0] - i; j++)
			{
			pq = Math.round((-j*xGridStep - a)*numX/(b-a));
// alert("j = " + j + "pq = " + pq);
			if ((pq <= numX) && (pq >= 0)) xGridLines[j+i] = pq;
			else xGridLines[j+i] = 0;
			} // loop j
		} // a and b have opposite sign

	if (c*d >= 0) {
		for (var i = 1; i <= yGridLines[0]; i++)
			{
			yGridLines[i] = Math.round( i*yGridStep*numY/(d-c)  )
			} // y Grid Line Positions
		} // if c and d have the same sign
	else {
		for (var i = 1; i <= yGridLines[0]; i++)
			{
			pq = Math.round((i*yGridStep - c)*numY/(d-c));
			if ((pq <= numY) && (pq >= 0)) yGridLines[i] = numY - pq;
			else break;
			} // loop i
		for (var j = 1; j <= yGridLines[0] - i; j++)
			{
			pq = Math.round((-j*yGridStep - c)*numY/(d-c));
// alert("j = " + j + "pq = " + pq);
			if ((pq <= numY) && (pq >= 0)) yGridLines[j+i] = numY - pq;
			else yGridLines[j+i] = 0;
			} // loop j
		} // if c and d have oppositve sign
	for (var i = 1; i <= xGridLines[0]; i++) 
		{
// alert("i = " + i + " : " + xGridLines[i] + "of " + xGridLines[0]);
		var p = xGridLines[i];
		if (p > 0) {
			for (var k = 1; k <= numY; k++) if (theCanvas[p][k] != lineColor) theCanvas[p][k] = 7;
			} // if p is positive
		} // i
// alert("here");
	for (var i = 1; i <= yGridLines[0]; i++) 
		{
		var p = yGridLines[i];
		if (p > 0) {
			for (var k = 1; k <= numX; k++) if (theCanvas[k][p] != lineColor) theCanvas[k][p] = 7;
			} // IF P IS POSITIVE
		} // i
	// now the axes
	if ((a < 0) && (b > 0)) yAxisPosition = Math.round((-a)*numX/(b-a));
else yAxisPosition = -1;
	if ((c < 0) && (d > 0)) xAxisPosition = Math.round(d*numY/(d-c));
else xAxisPosition = -1;
	if (yAxisPosition > 0) for (var k = 1; k <= numY; k++) theCanvas[yAxisPosition][k] = 1;
	if (xAxisPosition > 0) for (var k = 1; k <= numX; k++) theCanvas[k][xAxisPosition] = 1;
// alert("here");
} // oktoRoll	

} // makeGraph
// ******************** End Initialize Graph*****************

// ************** DISPLAY GRAPH *************
function displayTheGraph()  {
// puts up the graph in the appropriate window, and extra things as needed

	// *** Open a window
	var tb="toolbar=0,directories=0,status=0,menubar=0"
	tb+=",scrollbars=1,resizable=1,"
	var tbend="width=350,height=300"
	tb+=tbend
	Win_1 = window.open("","win1",tb);
	Win_1.document.open();
	// ** Window is opened

var theString  = '<html><title>Your Graph</title><body bgcolor = "FFFFFF"><p><center><table><tr>'
theString += '<td></td><td align = center>' + roundDec(d,4) + '</td></tr><tr><td valign = center>'+ roundDec(a,4)+'</td><td>';

theString += '<table border = 1><tr><td>';
theString += formatGraph2();			// the actual graph
theString += '</td></tr></table>';
theString += '</td><td valign = center>' + roundDec(b,4) + '</td></tr><tr><td><td align = center>' + roundDec(c,4) + '</td></tr></table></center></body></html>';
Win_1.document.write(theString);
Win_1.document.close();
Win_1.focus();

} // displayTheGraph
// ********** END DISPLAY GRAPH *************


// ************** FORMAT GRAPH *************
// Requires the following global variables:
// 1. theCanvas = the matrix of pixels -- entries are the colors 0-numCols
// 2. numX, numY, the dimensions of the canvas
// 3. theDot: an array of pixel images (gif) -- little swaths of color
// Returns the html for the actual graph

function formatGraph2() {
var theString = "";
var repeats = new Array(); // this contains a list of different rows in theCanvas
var theRow1 = new makeArray(numY);
repeats[1] = 1; // first row
var numRepeats = 1; 
var oldVal = 0;
var newVal = 0;
var theLength = 0;
var theHeight = 0;
for (var k = 1; k <= numY; k++) theRow1[k] = theCanvas[k][1];

for (var i = 1; i <= numY; i++)
	{
// if (i == 100) alert('Here i = ' + i);
	for (var j = i+1; j <= numY; j++)
		{
// if (j == 100) alert('Here j = ' + j);
		for (var k = 1; k <= numX; k++)
			{
// if (k == 100) alert('Here k = ' + k);
			if (theRow1[k] != theCanvas[k][j])
				{
				numRepeats++;
				repeats[numRepeats] = j;
				for (var p = 1; p <= numY; p++) theRow1[p] = theCanvas[p][j]; 
				i = j-1;
				k = numX;
				j = numY;
				} // if
			} // k
		} // j

	} // i
repeats[numRepeats+1] = numY+1; // for last line height
// var theTestString = '';
// for (var i = 1; i <= numRepeats; i++) theTestString += "," + repeats[i];
// document.theForm.output.value += theTestString;
// alert("here. numRepeats = " + numRepeats );
var theRow = 1;
for (var i = 1; i <= numRepeats; i++)
	{ 
	theLength = 1;
	theRow = repeats[i];
	theHeight = repeats[i+1] - repeats[i];
	oldVal = theCanvas[1][theRow];
	for (var j = 2; j <= numX; j++)
		{
		 if ((j == numX)  && ( theCanvas[j][theRow] != oldVal) )
			{
			var LL = theLength-1;
			theString += '<img src = "' + theDot[oldVal].src + '" width = ' + LL + ' height = ' + theHeight + '>'
			theString += '<img src = "' + theDot[theCanvas[j][theRow]].src + '" width = 1 height = ' + theHeight + '>'
			
			} // change on last pixel

		else if(( theCanvas[j][theRow] != oldVal) || (j == numX))
			{
			// saw a change
// alert(i);
// alert(j);
			// make a string of the right length
			theString += '<img src = "' + theDot[oldVal].src + '" width = ' + theLength + ' height = ' + theHeight + '>'
			oldVal = theCanvas[j][theRow];
			theLength = 0;
			} 
		theLength++;
		} // j
	theString += '<br>';
	} // i

return(theString);
} // formatGraph
// ********** END FORMAT GRAPH *************



// ************** SEGMENT *************
function clippedSegment(x1,y1,x2,y2, shadingDown){
// draws a segment on the global palette
// requires global window coords: xMax, yMax, xMin, yMin
// shadingDown = 0 for no shading 1 for shading down -1 for shading up

// if no shading check whether inside the window at all
// don't bother to plot it in this case.
if ((shadingDown == 0) && ((x1 >= b) || (x2 <= a))) return(0);
else if ((shadingDown == 0) && ((y1 >= d) & (y2 >= d))) return(0);
else 
	{
	// in range
	var xMin = a;
	var xMax = b;
	var yMin = c;
	var yMax = d; 


// counter++;
// if (counter == 5) alert("xMin = "+ xMin + "xMax = "+ xMax + "yMin = "+ yMin + " yMax = "+ yMax);


	var x1bar = Math.round( numX*(x1-xMin)/(xMax-xMin) );
	var x2bar = Math.round( numX*(x2-xMin)/(xMax-xMin) );
	var y1bar = Math.round( numY*(yMax-y1)/(yMax-yMin) );
	var y2bar = Math.round( numY*(yMax-y2)/(yMax-yMin) );

// Now draw the actual segment in the right color
	var deltaX = x2bar - x1bar;
	var deltaY = y2bar - y1bar; 

// alert(x2bar);
// alert(y2bar);
// alert(deltaX);
// alert(deltaY);

	var shading = true; // avoid shading same vertical line twice
	var xx = 0;
	var y = 0;
	var xadj = 0; // anti-aliasing neighbors
	var yadj = 0;
	var xold = 0;
	var yold = 0;
	var xint = 0;
	var yint = 0;
	var numSteps = 0;
	var theYStep = 0; 
	var theXStep = 0;
	var theColor = 0;
	// latter are actual steps on the screen 
	var xOK = false;
	var yOK = false;

	if ( Math.abs(deltaX) > Math.abs(deltaY) ) {numSteps = Math.abs(deltaX); xOK = true}
	else {numSteps = Math.abs(deltaY); yOK = true}
// alert(numSteps);
	theXStep = deltaX/numSteps;
	theYStep = deltaY/numSteps

// set initial pixel
xx = x1bar; y = y1bar; 
// alert("xx = " + xx + "y = " + y);
// if (x1 == 0) alert(xx);
if (( xx >= 1) && (y >= 1)  && (xx <= numX) && (y <= numY)) {theCanvas[xx][y] = lineColor} 
// alert(shadingDown);
// now do the shading under or above the initial pixel
// note that up is actually down in the canvas (like scren coordinates)
if (shadingDown == -1)
	{
	if ((xx >= 1) && (xx <= numX))
		{
		theColor = 2; // not feasible
		if (yint >= yMax) for (var k = 1; k <= numY; k++) 
			{
			if(theCanvas[xx][k] != lineColor) theCanvas[xx][k] = theColor;
			}
		else if  (( yint >= yMin) &&  (yint <= yMax)) for (var k = 1; k < yint; k++) 			{
			if(theCanvas[xx][k] != lineColor) theCanvas[xx][k] = theColor;
			}
		}
	}
else if (shadingDown == 1)
// shading down
	{
	if ((xx >= 1) && (xx <= numX))
		{

		theColor = 2; // not feasible
		if (yint <= 0) for (var k = 1; k <= numY; k++) 
			{
			if(theCanvas[xx][k] != lineColor) theCanvas[xx][k] = theColor;
			}
		else if  (( yint >= 1) &&  (yint <= numY)) for (var k = yint+1; k < numY; k++) 
			{
			if(theCanvas[xx][k] != lineColor) theCanvas[xx][k] = theColor;
			}
		}
	}

for (var i = 1; i <= numSteps; i++)
	{
// alert(xx);
// alert(y);
	xold = Math.round(xx); // to record whether x changed (for shading)
	yold = Math.round(y);
	xx += theXStep;  
	y += theYStep;
	xint = Math.round(xx);
	yint = Math.round(y);
	if (xint > xold) shading = true;
	else shading = false;

	if (( xint >= 1) && (yint >= 1)  && (xint <= numX) && (yint <= numY)) 	theCanvas[xint][yint] = lineColor;
	// now do anti-aliasing
	// sets adjacent pixels light if they are not already set in the
	// line color
	if ((xint != xold) && (yint != yold)) {
		for (var k = -1; k <= 1; k++) {
			for (var k2 = -1; k2 <= 1; k2++) {
				xadj = xint + k; 	
				yadj = yint + k2;
				if ((xadj >= 1) && (yadj >= 1)  && (xadj <= numX) && (yadj <= numY) &&  (theCanvas[xadj][yadj] == 0)) { theCanvas[xadj][yadj] = lineColorLite}
				} // k2
			} // k
		} // if both x and y changed
	// now do the shading
	if (shading)
		{
		if (shadingDown == -1) 
			{
			if ((xint >= 1) && (xint <= numX))
				{
				theColor = 2; // not feasible
				if (yint >= numY) for (var k = 1; k <= numY; k++) 
				{
				if(theCanvas[xint][k] != lineColor) theCanvas[xint][k] = theColor;
				}
				else if  (( yint >= 1) &&  (yint <= numY)) for (var k = 1; k < yint; k++)
				{
				if(theCanvas[xint][k] != lineColor) theCanvas[xint][k] = theColor;
				}
				}
			}
		else if (shadingDown == 1)
		// shading down
			{
// if (i == 1) alert("here");
			if ((xint >= 1) && (xint <= numX))
				{
				theColor = 2; // not feasible
				if (yint <= 0) for (var k = 1; k <= numY; k++) 
				{
				if(theCanvas[xint][k] != lineColor) theCanvas[xint][k] = theColor;
				}
				else if  (( yint >= 1) &&  (yint <= numY)) for (var k = yint+1; k < numY; k++)
				{
				if(theCanvas[xint][k] != lineColor) theCanvas[xint][k] = theColor;
				}

				}
			}
		} // if shading
	} // i
	} // if in range
} // segment
// ********** END SEGMENT *************


// **************** DRAWCURVE ******************************
// *** Draws a graph of a given funtion

function drawCurve(theyString, thexMin, thexMax) {
// alert("at DrawCurve; theyString = " + theyString + " thexMin = "+ thexMin + " thexMax = "+ thexMax);
// Draws curve using function clippedSegment(x1,y1,x2,y2,shadingDown) repeatedly
// a and b are Xmin and Xmax respectively
okToRoll = true;
var theStrg = myParse(theyString);
if (thexMax <= thexMin) {alert("Xmin must be less than Xmax. Correct this and try again."); okToRoll = false}
if (okToRoll) {
			// Search for a place to start the graph 
			// (in case initial y-coords are undefined or infinite.) 
			var deltax = (thexMax - thexMin)/numDivisions;
// alert(deltax);
			x1 = thexMin;
			x = x1;
			y1 = checkEval(theStrg);
// alert("x1 = " + x1 + "y1 = "+ y1 + (y1 > infinity));
			if(isBad(y1)) {
// alert("**HERE**")
				for (var k = thexMin; k <= thexMax; k+=deltax) {
					x = k;
					x1 = k;
					y1 = checkEval(theStrg);
					if(!isBad(y1)) break;
					} // end count
// alert(x1);
				if (k >= thexMax) {alert("Y is not defined for your given X-range."); okToRoll = false}
				if (y1 > infinity) y1 = infinity;
				else if (y1 < -infinity) y1 = -infinity;
				} // y1 is not a number
			

			} // if OKtoRoll

if (okToRoll) {
		// now plot the points

// alert(x);	

		var thecount = 0; /// testing
		var startingX = x + deltax;
		for (var k = startingX; k <= thexMax; k+=deltax) {
			thecount++;
// if (thecount == 35) alert("thcount = " + thecount + "k = "+k);
// if (thecount == 41) alert("thcount = " + thecount + "k = "+k);
			x2 = k;
			x = x2;
			y2 = checkEval(theStrg);
			if (!isBad(y2)) {
				if (y2 > infinity) y2 = infinity;
				else if (y2 < -infinity) y2 = -infinity; 
				var p = clippedSegment(x1,y1,x2,y2,0);
				x1 = x2;
				y1 = y2;
				} // If second point is defined
			else {
				for (var p = x2+deltax; p <= thexMax; p+=deltax) {
					x = p;
					y1 = checkEval(theStrg);
					if(!isBad(y1)) break;
					} // end count
				k = p; // reset k further along the loop
				x1 = p;  // y1 is already what it is supposed to be
				} // second point was undefined
				
			
if(thecount >= numDivisions + 2) k = thexMax+1;
			} // k
// alert("k over");
	} // if okToRoll
} // function drawCurve


// ************** various miscellaneous routines *************

function rememberValues() {
if (document.theFormC.y1x1.value != "") {
	var theInstruction = ""; // a javascript instuction
	var doIt = 0; // a dummy variable
	for (var i = 1; i <= maxnumX; i++) {
		for (var j = 0; j <= maxnum; j++) {
		if (j > 0) theInstruction = " theValues[i][j] = document.theFormC.y"+j+"x"+i+".value";
		else theInstruction = " theValues[i][j] = document.theFormC.x"+i+".value";
		doIt = eval(theInstruction);
		} // j
	} // i
} // IF THERE IS SOMETHING IN THE FIRST CELL
} // remember values

function bringBackValues() {
	if (document.theFormC.y1x1.value == "") {
		var theInstruction = ""; // a javascript instuction
		var doIt = 0; // a dummy variable
		for (var i = 1; i <= maxnumX; i++) {
			for (var j = 0; j <= maxnum; j++) {
			if (j > 0) theInstruction = " document.theFormC.y"+j+"x"+i+".value = theValues[i][j]";
			else theInstruction = " document.theFormC.x"+i+".value = theValues[i][j]";
			doIt = eval(theInstruction);
			} // j
		} // i
	} // if there is nothing in the first cell
} // remember values

