// With the exception of borrowing some names and the trivial routines stripSpaces, makeArray, makeArray2 and makeArray3, and getting permission
// from David Binner, david@akiti.ca, for some excellent functions that approximate eigenvalues and eigenvectors, I take full responsibility for
// the sad state of this code. It isn't pretty, slick or anything near optimal. However, for the most part, it works, and I hope someone makes some use of it.
// Please send any comments to Jeff Morgan at jmorgan@math.uh.edu.
var loghistory = "This is the log of activities:\n"; //This is a string that keeps track of the log of operations.
var showdisplay = 0; // is 0 if display has not been pushed to the log and is 1 if it has. Updated in writematrix(ii) when ii = 0.
var matnames = new Array();
matnames[0] = "Display";
matnames[1] = "A";
matnames[2] = "B";
matnames[3] = "C";
matnames[4] = "D";
matnames[5] = "E";
matnames[6] = "F";
matnames[7] = "u";
matnames[8] = "v";
matnames[9] = "w";
matnames[10] = "y";
matnames[11] = "z";
matnames[12] = "I";
matnames[14] = "the_Display_Matrix";
var maxRows = 20;
var maxCols = 20;
var rows = new makeArray(16); //keeps track of the number of rows of each matrix as we save them. rows[12] is the dimension of a created indentity matrix. 13, 14 and 15 are for dummies.
var cols = new makeArray(16); //keeps track of the number of columns of each matrix as we save them. cols[12] is the dimension of a created identity matrix.
// We will only determine the number of rows and columns of the display matrix when we are asked for a computation.
// rows and cols are determined by non empty entries.
var blankMatrix = new makeArray2(maxRows,maxCols); // Creates a blank matrix for clearing the spreadsheet
var hcount=0;
var vcount=0;  //keeps track of the scrolling of the table
var updatehold = 1; //allows updates with value 1. Does not with value 0.
var theMatrix = new makeArray3(16,maxRows,maxCols); 
//theMatrix[0][i][j] is the i,j entry in the spreadsheet, theMatrix[k] is A,B,C, etc..., theMatrix[12] is a maxRows x maxRows identity matrix. 13, 14, 15 and 16 are dummies.
makeidentity();  //turns theMatrix[12] into a maxRows x maxRows identity matrix

//**************** Make a random matrix with integer values between -10 and 10
function dorandom(){
	var m = document.theSpreadsheet.numrows.value;
	var n = document.theSpreadsheet.numcols.value;
	clearit();
	rows[0]=m;
	cols[0]=n;	
	var now = new Date();
	var seed = now.getSeconds();
	if (document.theSpreadsheet.randmat.value==1)
		{
		for (var i=1; i<=rows[0]; i++)
			{
			for (var j=1; j<=cols[0]; j++)
				{
				theMatrix[0][i][j]="" + eval(Math.floor(Math.random(seed)*21) - 10);
				}
			}
		}
	else
		{
		var maxmn = m;
		if (n>m) {maxmn = n;}
		// Create a maxmn by maxmn matrix with determinant +-1 by using LU factorization.
		// First create the lower triangular L in theMatrix[13] and an upper triangular U in theMatrix[14]
		// with +-1 on the diagonal.
		for (var i=1; i<=maxmn; i++)
			{
			for (var j=1; j<=maxmn; j++)
				{
				if (i<j) 
					{
					theMatrix[14][i][j]="" + eval(Math.floor(Math.random(seed)*9) - 4);
					theMatrix[13][i][j]="0";
					}
				if (i>j)
					{
					theMatrix[13][i][j]="" + eval(Math.floor(Math.random(seed)*9) - 4);
					theMatrix[14][i][j]="0";
					}
				if (i==j)
					{
					theMatrix[13][i][j]="" + Math.pow(-1,Math.floor(Math.random(seed)*2));
					theMatrix[14][i][j]="" + Math.pow(-1,Math.floor(Math.random(seed)*2));
					}
				}
			}
		// Now multiply L times U to get a nice maxmn by maxmn matrix.
		for (var i=1; i<=maxmn; i++)
			{
			for (var j=1; j<=maxmn; j++)
				{
				var fullsum = "0";
				for (var k=1; k<=maxmn; k++)
					{
					fullsum = add(fullsum,multiply(theMatrix[13][i][k],theMatrix[14][k][j]));
					}
				theMatrix[15][i][j] = fullsum;
				}
			}
		// Now load the mxn block of theMatrix[15] into theMatrix[0].
		for (var i=1; i<=m; i++)
			{
			for (var j=1; j<=n; j++)
				{
				theMatrix[0][i][j] = theMatrix[15][i][j];
				}
			}
		}
	reset();
}

//****************Show the log of activities
function doshowlog(){
	document.theSpreadsheet.worklog.value = loghistory+"";
	document.theSpreadsheet.worklog.scrollTop = document.theSpreadsheet.worklog.scrollHeight;
}

//****************Add a comment to the log of activities
function docomment(){
	loghistory = loghistory + document.theSpreadsheet.workcomment.value + "\n";
	doshowlog();
	document.theSpreadsheet.workcomment.value = "";
}

//****************Clear the log of activities
function doclearlog() {
	var confirmanswer = confirm("Are you sure you want to delete the log?");
	if (confirmanswer) 
		{ 
		loghistory = "This is the log of activities:\n";
		document.theSpreadsheet.worklog.value = "The log was deleted.";
		}
	else 
		{
		document.theSpreadsheet.worklog.value = "The log was not deleted.";
		}
}

// ***************Make theMatrix[12] into a maxRows x maxRows identity matrix
function makeidentity() {
	for (var i = 1; i<=maxRows; i++)
		{
		for (var j=1; j<=maxRows; j++)
			{
			if (i==j) {theMatrix[12][i][j]="1";} else {theMatrix[12][i][j]="0";}
			}
		}
}

//****************Write a matrix into a text string.
//****************It is assumed that the matrix is not empty.
function writematrix(ii) {
	var junk = "";
	for (var i=1; i<=rows[ii]; i++)
		{
		for (var j=1; j<=cols[ii]; j++)
			{
			junk = junk + theMatrix[ii][i][j];
			if (j < cols[ii]) {junk = junk + ",   ";}
			}
		junk = junk + ";\n"
		}
	if (ii==0) {showdisplay = 1;}
	else {showdisplay = 0;}
	return junk;
}

// ***************Takes care of either deleting a row or a column. document.theSpreadsheet.delrowcol.value is 1 for row and 2 for column. document.theSpreadsheet.delrowcolnum.value
// ***************gives the row or column to be delelted.
function dodelrowcol() {
	var k = eval(document.theSpreadsheet.delrowcolnum.value); //The row or column number to delete.
	if (document.theSpreadsheet.delrowcol.value=="1")
		{ // We are deleting a row.
		for (var j=1; j<=maxCols; j++)
			{
			for (var i=k; i<maxRows; i++)
				{
				theMatrix[0][i][j] = theMatrix[0][i+1][j];
				}
				theMatrix[0][maxRows][j] = "";
			}
		}
	else
		{ // We are deleting a column.
		for (var i=1; i<=maxRows; i++)
			{
			for (var j=k; j<maxCols; j++)
				{
				theMatrix[0][i][j] = theMatrix[0][i][j+1];
				}
				theMatrix[0][i][maxCols] = "";
			}

		}
	showdisplay = 0;
	reset();
}

// ***************Organizes calls for computing the transpose, determinant, rref, inverse, eigenvalues, eigenvectors, charpoly, Nullspace, RangeSpace, trace and LSQ-RREF of A, B, ..., z.
// ***************The results is sent to the display.
function dofofmat() {
	var i1 = document.theSpreadsheet.mat.value; // The matrix we are operating on.
	// If i1 = "14" then the request is being made on the display matrix. We'll have to make sure it is legal and then give the values for rows[14] and cols[14].
	if (i1=="14")
		{
		// ************************ Verifying the display matrix and moving it to theMatrix[14]
		var checkrational = 1;
		var k = 14;
		rows[k] = 0;
		cols[k] = 0;
		for (var i=1; i<=20; i++)
			{
			for (var j=1; j<=20; j++)
				{
				if (stripSpaces(theMatrix[0][i][j]) != "")  //check if we are at a larger row or column location than the current values in rows[k] and cols[k]
					{
					if (isrational(theMatrix[0][i][j]) == "NOT") {checkrational = 0;}
					if (i > rows[k]) {rows[k] = i;}
					if (j > cols[k]) {cols[k] = j;}
					}
				}
			}
		if (checkrational == 1)
			{
			// Now fill in the zeros
			for (var i=1; i<=20; i++)
				{
				for (var j=1; j<=20; j++)
					{
					if ((i > rows[k]) || (j > cols[k])) {theMatrix[k][i][j] = "";}
					else
						{
						if (stripSpaces(theMatrix[0][i][j]) == "") 
							{
							theMatrix[k][i][j] = "0";
							}
						else
							{
							theMatrix[k][i][j] = isrational(theMatrix[0][i][j]);
							}
						}
					}
				}
				loghistory = loghistory + matnames[k] + " is now\n";
				loghistory = loghistory + writematrix(k);
				doshowlog();
				showdisplay = 1;
			}
		else alert('The entries in the display are not all rational numbers.');		
		// ************************
		}
	if (rows[i1]=="" || checkrational==0) {alert('The matrix is either empty or it contains illegal entries.');}
	else
	{
	var i2 = document.theSpreadsheet.funcmat.value; // The operation.
	// i2 = 11 requests the characteristic polynomial of the matrix.
	if (i2 == "11")
		{
		// We need to make sure the matrix is square.
		if ((rows[i1]!=cols[i1]) || (rows[i1]>10)) {alert('Either the matrix is not square, or it is larger than 10x10.')}
		else
			{ //The matrix is square. Let's start computing.
			var n = rows[i1];
			// We do separate cases for n = 1, n = 2, n = 3 and n >= 4.
			// In most cases, we will need the trace.
			var tracesum = "0";
			var m = rows[i1];
			if (rows[i1]>cols[i1]) {m = cols[i1];}
			for (var i=1; i<=m; i++)
				{
				tracesum = add(tracesum,theMatrix[i1][i][i]);
				}
			//The value of the trace is now in the variable tracesum.
			//We will need the determinant in most cases.
			var detval = mref(i1); // We have the multiplier. Now we need the product of the diagonal entries.
			for (var k=1; k<=rows[i1]; k++)
				{
				detval = multiply(detval,theMatrix[0][k][k]);
				}
			//The value of the determinant is now in the variable detval. This value and tracesum will allow us to create
			//the charpoly for n=1,2. Acutally, n=1 is trivial.
			var textcharpoly = "The characteristic polynomial of "+matnames[i1]+" is given by\n";
			if (n==1)
				{
				textcharpoly = textcharpoly + "det( " + matnames[i1]+" - z I ) = " + theMatrix[i1][1][1] + " - z\n";
				}
			if (n==2)
				{
				textcharpoly = textcharpoly + "det( " + matnames[i1]+" - z I ) = " + detval + " - " + "(" + tracesum + ") z + z^2\n";
				}
			if (n>=3)
				{
				//We need some zi values so that we can compute det(Mat - zi*I) for i=1..(n-2). Note that since n<=10 we need at most 8 values.
				var zvals = new makeArray(8);
				zvals[1] = 1;
				zvals[2] = -1;
				zvals[3] = 2;
				zvals[4] = -2;
				zvals[5] = 3;
				zvals[6] = -3;
				zvals[7] = 4;
				zvals[8] = -4;
				//Now we create the i-th row of augmented matrix that must be solved, for i = 1 to n-2. 
				//The augmented matrix will be loaded into theMatrix[15], and the dimensions will be (n-2)x(n-1).
				rows[15] = n-2;
				cols[15] = n-1;
				rows[16] = n; //We will need this for the computation of det(theMatrix[i1] - zI).
				cols[16] = n;
				//Move theMatrix[i1] into theMatrix[16] so that we can create theMatrix[i1] - zI there by simply perturbing the diagonal entries.
				for (var i=1; i<=n; i++)
					{
					for (var j=1; j<=n; j++)
						{
						theMatrix[16][i][j] = theMatrix[i1][i][j];
						}
					}
				for (var i=1; i<=n-2; i++)
					{
					var gval = multiply("-1",detval);
					var z = zvals[i];
					// We need to compute g(z) = det(theMatrix[i1] - zI) - detval + (-1)^n * tracesum * z^(n-1) + (-1)^(n+1) * z^n.
					//Keep in mind that detval and tracesum are strings that could possibly be non-interger rational numbers. So, use add() and multiply().
					//Let's get det(theMatrix[i1] - zI). First we need to perturb the diagonal entries of theMatrix[16].
					var minusz = multiply("-1",""+z);
					for (var j=1; j<=n; j++)
						{
						theMatrix[16][j][j] = add(theMatrix[i1][j][j],minusz);
						}
					//Now we get the determinant.
					var detvali = mref(16); // We have the multiplier. Now we need the product of the diagonal entries.
					for (var k=1; k<=rows[16]; k++)
						{
						detvali = multiply(detvali,theMatrix[0][k][k]);
						}
					// det(theMatrix[i1] - zI) is now stored in detvali. Let's finish gval.
					gval = add(detvali,gval);
					gval = add(gval,multiply(tracesum,""+eval(Math.pow(-1,n)*Math.pow(z,n-1))));
					gval = add(gval,""+eval(Math.pow(-1,n+1)*Math.pow(z,n)));
					//That's the full gval that we need. Now let's build the i-th row of the augmented matrix and put it in theMatrix[15].
					for (var j=1; j<=n-2; j++)
						{
						theMatrix[15][i][j] = ""+Math.pow(z,j);
						}
					theMatrix[15][i][n-1] = gval;
					} 
				//We are done building theMatrix[15] as an (n-2)x(n-1) matrix. We need to RREF this.
				mrref(15); //This loads the rref of theMatrix[15] into theMatrix[0] and calls reset.
				textcharpoly = textcharpoly + "det( " + matnames[i1]+" - z I ) = " + detval; 
				for (var j=1; j<=n-2; j++)
					{
					textcharpoly = textcharpoly + " + (" + theMatrix[0][j][n-1] + ") z"; 
					if (j!=1) {textcharpoly = textcharpoly + "^" + j;} 
					}
				textcharpoly = textcharpoly + " + (" + multiply(tracesum,""+Math.pow(-1,n-1)) + ") z^" + eval(n-1) + " + (" + Math.pow(-1,n) + ") z^" + n + "\n";
				}
			clearit();
			theMatrix[0][1][1] = "See text.";
			reset();
			loghistory = loghistory + textcharpoly;
			doshowlog();
			}
		}
	// i2 = 10 requests the LSQ-RREF of the matrix.
	if (i2 == "10")
		{
		if (cols[i1]<2) alert('The matrix must have at least 2 columns.');
		else
			{
			// We need to create the matrix given by the first  cols[i1] - 1  columns. Then we form its transpose and multiply the transpose by
			// theMatrix[i1]. Then, we perform RREF on the result.
			// We create the transpose from the start and put it in theMatrix[13].
			rows[13] = cols[i1] - 1;
			cols[13] = rows[i1];
			for (var i=1; i<=rows[13]; i++)
				{
				for (var j=1; j<=cols[13]; j++)
					{
					theMatrix[13][i][j] = theMatrix[i1][j][i];
					}
				}
			// Now we multiply theMatrix[13] by theMatrix[i1] and put the result in theMatrix[15].
			for (var i=1; i<=rows[13]; i++)
				{
				for (var j=1; j<=cols[i1]; j++)
					{
					//multiply the i-th row of theMatrix[13] by the j-th column of theMatrix[i1]
					var rowsum = "0";
					for (var k=1; k<=cols[13]; k++)
						{
						rowsum = add(rowsum,multiply(theMatrix[13][i][k],theMatrix[i1][k][j]));
						}
					theMatrix[15][i][j]=rowsum;
					}
				}
			rows[15]=rows[13];
			cols[15]=cols[i1];
			// OK. Now we just call mrref(15).
			mrref(15); // This performs the RREF of theMatrix[15], loads it into theMatrix[0], and calls reset().
			// Now write the activity to the log.
			fill(); // To make sure that rows[0] and cols[0] are defined.
			loghistory = loghistory + "The LSQ-RREF of "+matnames[i1]+" is\n";
			loghistory = loghistory + writematrix(0);
			doshowlog();
			}
		}
	// i2 = 1 requests the transpose of theMatrix[i1]
	if (i2 == "1")
		{
		mtranspose(i1);
		fill(); // To make sure that rows[0] and cols[0] are defined.
		loghistory = loghistory + "The transpose of "+matnames[i1]+" is\n";
		loghistory = loghistory + writematrix(0);
		doshowlog();
		}
	//document.theSpreadsheet.funcmat.value=2 is eigval
	if (i2 == "2")
		{
		// Check to make sure theMatrix[i1] is not empty and is square.
		if (rows[i1]!=0 && rows[i1]==cols[i1])
			{
			EigSolve(i1,0);
			clearit();
			theMatrix[0][1][1] = "See text area.";
			reset();
			}
		else { alert("The matrix is either empty or it is not square."); }
		}
	//document.theSpreadsheet.funcmat.value=7 is eigvects
	if (i2 == "7")
		{
		// Check to make sure theMatrix[i1] is not empty and is square.
		if (rows[i1]!=0 && rows[i1]==cols[i1])
			{
			EigSolve(i1,1);
			clearit();
			theMatrix[0][1][1] = "See text area.";
			reset();
			}
		else { alert("The matrix is either empty or it is not square."); }
		}
	//document.theSpreadshett.funcmat.value=9 is NullSpace
	if (i2=="9")
		{
		// Make sure the selected matrix is not empty.
		if (rows[i1]=="") {alert('The matrix is empty.');}
		else
			{
			// Start by creating the rref of the requested matrix. This will write to theMatrix[0].
			mrref(i1);
			var m = rows[i1];
			var n = cols[i1];
			for (var i=1; i<=n; i++)
				{
				for (var j=1; j<=n; j++)
					{
					theMatrix[13][i][j]="0";
					}
				}
			// Now theMatrix[13] is an nxn zero matrix.
			// We need to "roll" the rows from theMatrix[0] into theMatrix[13] if necessary to get the leading 1's on the diagonal.
			for (var i=1; i<=n; i++)
				{ // Go through and find a leading one if it exists. It will occur somewhere in the i,i to i,n slot, if at all.
				var k = 0;
				for (j=i; j<=n; j++)
					if ((theMatrix[0][i][j]=="1") && (k==0))
						{
						// There is a leading 1. Copy this row into theMatrix[13][j].
						k = 1;
						for (var l=j; l<=n; l++)
							{
							theMatrix[13][j][l] = theMatrix[0][i][l];
							}
						}
				}
			// Now theMatrix[13] has all of the info from the rref, with the leading 1's rolled onto the diagonal.
			// It's time to create the basis for the null space from the data in theMatrix[13]. Since we are done with theMatrix[13], I will clear it now.
			clearit();
			var basiscount = 0;
			// Check through all of the diagonal entries of theMatrix[13]. If theMatrix[13][i][i]==0 then set basiscount+=1, put a -1 in this slot and place the new column in
			// theMatrix[0][k][basiscount] for k=1,...,n. 
			for (var i=1; i<=n; i++)
				{
				if (theMatrix[13][i][i]=="0")
					{
					theMatrix[13][i][i]="-1";
					basiscount = basiscount + 1;
					for (var k=1; k<=n; k++)
						{
						theMatrix[0][k][basiscount]=multiply("-1",theMatrix[13][k][i]);
						}
					}
				}
			// If basiscount=0 then the null space is trivial. Otherwise, the basiscount columns of theMatrix[0] contain the basis for the null space.
			if (basiscount==0)
				{
				loghistory = loghistory + "The null space of " + matnames[i1] + " contains only the zero vector.\n";
				theMatrix[0][1][1]="Trivial";
				reset();
				doshowlog();
				}
			else
				{
				rows[0]=n;
				cols[0]=basiscount;
				loghistory = loghistory + "The null space of " + matnames[i1] + " is " + basiscount + " dimensional, and a basis is given by the columns of the matrix\n";
				loghistory = loghistory + writematrix(0);
				reset();
				doshowlog();
				}
			}
		}
	//document.theSpreadsheet.funcmat.value=8 is RangeSpace
	if (i2=="8")
		{
		if (rows[i1]=="") {alert('The matrix is empty.');}
		else
			{
			mtranspose(i1); // the transpose of theMatrix[i1] is now in theMatrix[0]
			rows[13]=cols[i1];
			cols[13]=rows[i1];
			//Load this into theMatrix[13], set the values of rows[13] and cols[13], and call mrref(13).
			for (var i=1; i<=rows[13]; i++)
				{
				for (var j=1; j<=cols[13]; j++)
					{
					theMatrix[13][i][j]=theMatrix[0][i][j];
					}
				}
			mrref(13); // rref has been performed on the matrix and the result is written to theMatrix[0].
			// Now transpose the result back into theMatrix[13]. 
			var nonzerofinder = 0;
			for (var i=1; i<=rows[i1]; i++)
				{
				for (var j=1; j<=cols[i1]; j++)
					{
					theMatrix[13][i][j]=theMatrix[0][j][i];
					if (theMatrix[13][i][j]!="0") {nonzerofinder=1;} //There is a nonzero entry.
					else {theMatrix[13][i][j]="";} //Turns "0" to ""
					}
				}
			clearit(); // Clears theMatrix[0].
			for (var i=1; i<=rows[i1]; i++)
				{
				for (var j=1; j<=cols[i1]; j++)
					{
					theMatrix[0][i][j]=theMatrix[13][i][j];
					}
				}
			fill(); 
			// If nonzerofinder = 0 then the rangespace is trivial.
			if (nonzerofinder==0)
				{
				theMatrix[0][1][1] = "Trivial";
				reset();
				loghistory = loghistory + "The Range Space of " + matnames[i1] + " only contains the zero vector.\n";
				doshowlog();
				}
			// If nonzerofinder = 1 then we have a basis.
			else
				{
				reset();
				loghistory = loghistory + "The Range Space of " + matnames[i1] + " is " + cols[0] + " dimensional, and a basis is given by the column vectors of\n";
				loghistory = loghistory + writematrix(0);
				doshowlog();
				}
			}
		}
	// document.theSpreadsheet.funcmat.value=6 is trace
	if (i2 == "6")
		{
		// Check to make sure theMatrix[i1] is not empty.
		if (rows[i1]!=0)
			{
			var tracesum = "0";
			var m = rows[i1];
			if (rows[i1]>cols[i1]) {m = cols[i1];}
			for (var i=1; i<=m; i++)
				{
				tracesum = add(tracesum,theMatrix[i1][i][i]);
				}
			clearit();
			theMatrix[0][1][1] = tracesum;
			reset();
			loghistory = loghistory + "The trace of "+matnames[i1]+" is "+tracesum+".\n";
			doshowlog();
			}
		else { alert("The matrix is empty."); }
		}
	//document.theSpreadsheet.funcmat.value=3 is determinant
	if (i2 == "3")
		{
		if (rows[i1] != cols[i1]) {alert('The matrix is not square.');}
		else
			{
			var detval = mref(i1); // We have the multiplier. Now we need the product of the diagonal entries.
			for (var k=1; k<=rows[i1]; k++)
				{
				detval = multiply(detval,theMatrix[0][k][k]);
				}
				clearit();
				theMatrix[0][1][1] = detval;
				reset();
				fill(); // To make sure that rows[0] and cols[0] are defined.
				loghistory = loghistory + "The determinant of "+matnames[i1]+" is\n";
				loghistory = loghistory + writematrix(0);
				doshowlog();
			}
		}
	//document.theSpreadsheet.funcmat.value=4 is rref
	if (i2 == "4")
		{
		mrref(i1);
		reset();
		fill(); // To make sure that rows[0] and cols[0] are defined.
		loghistory = loghistory + "The rref of "+matnames[i1]+" is\n";
		loghistory = loghistory + writematrix(0);
		doshowlog();
		}
	//document.theSpreadsheet.funcmat.value=5 is inverse
	if (i2 == "5")
		{
		// First, make sure the matrix is square.
		if (rows[i1] != cols[i1]) {alert('The matrix is not square.');}
		else
			{ // The matrix is square. Load it into theMatrix[13], augment with the identity and call mrref.
			for (var i=1; i<=rows[i1]; i++)
				{
				for (var j=1; j<=rows[i1]; j++)
					{
					theMatrix[13][i][j] = theMatrix[i1][i][j];
					theMatrix[13][i][j+rows[i1]] = theMatrix[12][i][j];
					}
				}
			rows[13] = rows[i1];
			cols[13] = 2*cols[i1];
			mrref(13); // The rref is now in theMatrix[0]. Check to see if the matrix is nonsingular.
			var singcheck = 1;
			for (var k=1; k<=rows[i1]; k++)
				{
				if (theMatrix[0][k][k] == "0") {singcheck = 0;}
				}
			// If singcheck is still 1 then the inverse is in the last rows[i1] columns of theMatrix[0].
			if (singcheck == 0) {alert('The matrix is singular.');clearit();}
			else
				{
				for (var i=1; i<=rows[i1]; i++)
					{
					for (var j=1; j<=rows[i1]; j++)
						{
						theMatrix[0][i][j] = theMatrix[0][i][j+rows[i1]];
						theMatrix[0][i][j+rows[i1]] = "";
						}
					}
				reset();
				fill(); // To make sure that rows[0] and cols[0] are defined.
				loghistory = loghistory + "The inverse of "+matnames[i1]+" is\n";
				loghistory = loghistory + writematrix(0);
				doshowlog();
				}
			}
		}	
	//document.theSpreadsheet.funcmat.value=6 is rank
	//document.theSpreadsheet.funcmat.value=7 is nullity
	}
}

function mtranspose(k) {
	// computes the tranpose of theMatrix[k], loads it into theMatrix[0] and calls reset().
	// first we load the transpose into theMatrix[13] for temporary storage
	for (var i=1; i<=rows[k]; i++)
		{
		for (var j=1; j<=cols[k]; j++)
			{
			theMatrix[13][j][i] = theMatrix[k][i][j];
			}
		}
	// now we clear the display and clear theMatrix[0]
	clearit();
	// now we load theMatrix[13] into theMatrix[0] and call reset();
	for (var i=1; i<=cols[k]; i++)
		{
		for (var j=1; j<=rows[k]; j++)
			{
			theMatrix[0][i][j] = theMatrix[13][i][j];
			}
		}
	reset();
}

function mref(ii) {
	// computes the ref of theMatrix[ii], loads it into theMatrix[0] and returns the row swap multiplier (-1 or 1). It does not call reset(). This will be used by mdet, mrref and mrank.
	var swapval = "1"; // initially there are no row swaps
	// Start by loading theMatrix[ii] into theMatrix[0]
	clearit();
	rows[0] = rows[ii];
	cols[0] = cols[ii];
	for (var i=1; i<=rows[ii]; i++)
		{
		for (var j=1; j<=cols[ii]; j++)
			{
			theMatrix[0][i][j] = theMatrix[ii][i][j];
			}
		}
	// We work from the first column to the next to last column
	var pe = 1; // pe stands for pivot entry
	var iii = cols[ii]-1;
	for (var k=1; k<=iii; k++)
		{
		// Find the first nonzero entry in the k-th column (if one exists), from the pe-th row down.
		var foundit = 0;  // This is a flag that will hold the entry of the first nonzero entry if it is found.
		for (var i=pe; i<=rows[ii]; i++)
			{
			if ((theMatrix[0][i][k] != "0") && (foundit == 0)) {foundit = i;}
			}
		// If we found a nonzero entry then we bring it to the pe,k position by doing a row swap.
		if (foundit != 0) 
			{ // there is a nonzero entry
			if (foundit != pe) // swap the rows and change swapval
				{
				swapval = multiply("-1",swapval);
				var a = ""; //for holding values
				for (var j=k; j<=cols[ii]; j++)
					{
					a = theMatrix[0][pe][j];
					theMatrix[0][pe][j] = theMatrix[0][foundit][j];
					theMatrix[0][foundit][j] = a;
					}
				}
			// Now kill the things below the pe,k entry.
			for (var i=pe+1; i<=rows[ii]; i++)
				{
				// Go one row at a time. Replacing row i with -theMatrix[0][i][k]/theMatrix[0][pe][k]*(row pe) + (row i)
				var n = multiply("-1",invert(theMatrix[0][pe][k]));
				var m = multiply(n,theMatrix[0][i][k]);
				for (var j=k+1; j<=cols[ii]; j++)
					{
					theMatrix[0][i][j] = add(multiply(m,theMatrix[0][pe][j]),theMatrix[0][i][j]);
					}
				theMatrix[0][i][k] = "0"; // This will be automatic from the multiplier
				}
			// update the value of pe
			pe = pe+1;
			}
		}
	return swapval;
}


function mrref(ii) {
	// computes the rref of theMatrix[ii], loads it into theMatrix[0] and calls reset().
	// Start by loading theMatrix[ii] into theMatrix[0]
	clearit();
	rows[0] = rows[ii];
	cols[0] = cols[ii];
	for (var i=1; i<=rows[ii]; i++)
		{
		for (var j=1; j<=cols[ii]; j++)
			{
			theMatrix[0][i][j] = theMatrix[ii][i][j];
			}
		}
	// We work from the first column to the next to last column
	var pe = 1; // pe stands for pivot entry
	var iii = cols[ii];
	for (var k=1; k<=iii; k++)
		{
		// Find the first nonzero entry in the k-th column (if one exists), from the pe-th row down.
		var foundit = 0;  // This is a flag that will hold the entry of the first nonzero entry if it is found.
		for (var i=pe; i<=rows[ii]; i++)
			{
			if ((theMatrix[0][i][k] != "0") && (foundit == 0)) {foundit = i;}
			}
		// If we found a nonzero entry then we bring it to the pe,k position by doing a row swap.
		if (foundit != 0) 
			{ // there is a nonzero entry
			if (foundit != pe) // swap the rows
				{
				var a = ""; //for holding values
				for (var j=k; j<=cols[ii]; j++)
					{
					a = theMatrix[0][pe][j];
					theMatrix[0][pe][j] = theMatrix[0][foundit][j];
					theMatrix[0][foundit][j] = a;
					}
				}
			// Now kill the things above and below the pe,k entry.
			// First multiply the pe row to make the pe,k entry 1.
			for (var jj=k+1; jj<=cols[ii]; jj++) {theMatrix[0][pe][jj] = multiply(theMatrix[0][pe][jj],invert(theMatrix[0][pe][k]));}
			theMatrix[0][pe][k] = "1"; // This would be the result on this entry.
			for (var i=1; i<=rows[ii]; i++)
				{
				if (i != pe)
					{
					// Go one row at a time. Replacing row i with -theMatrix[0][i][k]*(row pe) + (row i)
					var m = multiply("-1",theMatrix[0][i][k]);
					for (var j=k+1; j<=cols[ii]; j++)
						{
						theMatrix[0][i][j] = add(multiply(m,theMatrix[0][pe][j]),theMatrix[0][i][j]);
						}
					theMatrix[0][i][k] = "0"; // This will be automatic from the multiplier
					}
				}
			// update the value of pe
			pe = pe+1;
			}
		}
}

// ***************handles the algebraic operations associated with alpha*A + beta*B, alpha*A - beta*B, alpha*A * beta*B, alpha*A 'Aug' beta*B
// ***************the result is sent to the display. The first matrix can be A, B, ..., z. The second can be these or the identity.
function domatop() {
	updatehold = 0;
	// First: Make sure that document.theSpreadsheet.pman.value and document.theSpreadsheet.nman.value are rational.
	var a = isrational(document.theSpreadsheet.pman.value);
	var b = isrational(document.theSpreadsheet.nman.value);
	if (a == "NOT" || b == "NOT")
		{ //illegal entries in pman or nman
		alert('Illegal entries.');
		}
	else
		{ //the multipliers are legal. Proceed. Multiplier values are stored in a and b.
			// Second: Check the size compatibility.
			// Start by getting the matrices for matone and mattwo.
			var i1 = document.theSpreadsheet.matone.value;
			var i2 = document.theSpreadsheet.mattwo.value;
			// Now see what operation was requested.
			if (document.theSpreadsheet.operation.value == 0 || document.theSpreadsheet.operation.value == 1)
				{
				// "+" or "-" was selected.
				// Check size compatibility. If i2 is 12 (i.e. the identity) then i1 has to be square.
				// In general, i1 and i2 have to be the same size.
				if (i2==12 && rows[i1]!=cols[i1])
					{
					alert('Sizes are not compatible.');
					}
				else
					{ // everything is ok to go
					clearit();
					rows[12]=rows[i1];
					cols[12]=rows[i1]; // just in case i2=12
					// Make sure the sizes are compatible.
					if (rows[i1]!=rows[i2] || cols[i1]!=cols[i2])
						{
						alert('Sizes are not compatible.');
						}
					else
						{ // Houston, we are good to go!
						if (document.theSpreadsheet.operation.value == 0) {var c="1"; var csym=" + ";} else {var c="-1"; var csym=" - ";}
						for (var i=1; i<=rows[i1]; i++)
							{
							for (var j=1; j<=cols[i1]; j++)
								{
								var m=theMatrix[i1][i][j];
								var n=theMatrix[i2][i][j];
								// Put a*m + b*n in theMatrix[0][i][j] 
								theMatrix[0][i][j]=isrational(eval(numer(a)*numer(m)*denom(b)*denom(n)+c*numer(b)*numer(n)*denom(a)*denom(m))+"/"+eval(denom(a)*denom(m)*denom(b)*denom(n)));
								}
							}
						reset();
						fill(); // To make sure that rows[0] and cols[0] are defined.
						loghistory = loghistory + "("+a+")"+matnames[i1] + csym + "("+b+")" + matnames[i2] + " is\n";
						loghistory = loghistory + writematrix(0);
						doshowlog();
						}
					}
				}
			if (document.theSpreadsheet.operation.value == 2)
				{
				// "*" was selected.
				// Check size compatibility. If i2 is 12 (i.e. the identity) then set rows[12] and cols[12] equal cols[i1]
				if (i2==12) {rows[12]=cols[i1]; cols[12]=cols[i1];} 
				// In general, cols[i1] and rows[i2] have to be the same size.
				if (cols[i1]!=rows[i2])
					{
					alert('Sizes are not compatible.');
					}
				else
					{
					// We are good to go.
					clearit();
					for (var i=1; i<=rows[i1]; i++)
						{
						for (var j=1; j<=cols[i2]; j++)
							{
							// Update theMatrix[0][i][j] by computing sum(a*theMatrix[i1][i][k]*b*theMatrix[i2][k][j],k=1..rows[i2])
							var rowsum="0";
							for (var k=1; k<=rows[i2]; k++)
								{
								var m=theMatrix[i1][i][k];
								var n=theMatrix[i2][k][j];
								var y=numer(a)*numer(m)*numer(b)*numer(n);
								var x=denom(a)*denom(m)*denom(b)*denom(n);
								rowsum=isrational(eval(numer(rowsum)*x+denom(rowsum)*y)+"/"+eval(denom(rowsum)*x));
								}
							theMatrix[0][i][j]=rowsum;
							}
						}
					reset();
					fill(); // To make sure that rows[0] and cols[0] are defined.
					loghistory = loghistory + "("+a+")"+matnames[i1] + "*" + "("+b+")" + matnames[i2] + " is\n";
					loghistory = loghistory + writematrix(0);
					doshowlog();
					}
				}
			if (document.theSpreadsheet.operation.value == 3)
				{
				// "Aug" was selected.
				// Check size compatibility. If i2 is 12 (i.e. the identity) then set rows[12] and cols[12] equal rows[i1]
				if (i2==12) {rows[12]=rows[i1]; cols[i2]=rows[i1];}
				// In general, rows[i1] and rows[i2] have to be the same size.
				if (rows[i1]!=rows[i2]) 
					{
					alert('Sizes are not compatible.');
					}
				else
					{
					// put the matrices together, load into theMatrix[0], and  call reset();
					clearit();
					for (var i=1; i<=rows[i1]; i++)
						{
						for (var j=1; j<=cols[i1]; j++)
							{
							theMatrix[0][i][j]=isrational(eval(numer(a)*numer(theMatrix[i1][i][j]))+"/"+eval(denom(a)*denom(theMatrix[i1][i][j])));
							}
						for (var k=1; k<=cols[i2]; k++)
							{
							theMatrix[0][i][k+cols[i1]]=isrational(eval(numer(b)*numer(theMatrix[i2][i][k]))+"/"+eval(denom(b)*denom(theMatrix[i2][i][k])));
							}
						}
					reset();
					fill(); // To make sure that rows[0] and cols[0] are defined.
					loghistory = loghistory + "("+a+")"+matnames[i1] + " augmented with " + "("+b+")" + matnames[i2] + " is\n";
					loghistory = loghistory + writematrix(0);
					doshowlog();
					}
				}
		}
	updatehold = 1;
}

// ***************Scroll the table right, left, up, down or reset to the beginning*****************
// ***************This could be done in one function instead of 5.*********************************

function right() {
updatehold = 0;
if (document.theSpreadsheet[7].value < 20)
	{
	hcount = hcount + 1; // increasing the value for scrolling to the right
	// change the column headers
	for (var i = 0; i<=7; i++)
		{
		document.theSpreadsheet[i].value = eval(document.theSpreadsheet[i].value) + 1;
		}
	// change the column entries
	for (var k = 0; k<=5; k++)
		{
		for (var j = 0; j<=7; j++)
			{
			document.theSpreadsheet[9+k*9+j].value = theMatrix[0][vcount+k+1][hcount+j+1];
			}
		}
	}
updatehold = 1;
} 

function left() {
updatehold = 0;
if (document.theSpreadsheet[0].value > 1)
	{
	hcount = hcount - 1; // decreasing the value for scrolling to the right
	// change the column headers
	for (var i = 0; i<=7; i++)
		{
		document.theSpreadsheet[i].value = eval(document.theSpreadsheet[i].value) - 1;
		}
	// change the column entries
	for (var k = 0; k<=5; k++)
		{
		for (var j = 0; j<=7; j++)
			{
			document.theSpreadsheet[9+k*9+j].value = theMatrix[0][vcount+k+1][hcount+j+1];
			}
		}
	}
updatehold = 1;
}

function down() {
updatehold = 0;
if (document.theSpreadsheet[8].value < maxRows-5)
	{
	vcount = vcount + 1; // increasing the value for scrolling to the right
	// change the row headers
	for (var i = 0; i<=5; i++)
		{
		document.theSpreadsheet[8+9*i].value = eval(document.theSpreadsheet[8+9*i].value) + 1;
		}
	//change the rows
	for (var k = 0; k<=5; k++)
		{
		for (var j = 0; j<=7; j++)
			{
			document.theSpreadsheet[9+k*9+j].value = theMatrix[0][vcount+k+1][hcount+j+1];
			}
		}
	}
updatehold = 1;
} 

function up() {
updatehold = 0;
if (document.theSpreadsheet[8].value > 1)
	{
	vcount = vcount - 1; // decreasing the value for scrolling to the right
	// change the row headers
	for (var i = 0; i<=5; i++)
		{
		document.theSpreadsheet[8+9*i].value = eval(document.theSpreadsheet[8+9*i].value) - 1;
		}
	// change the entries
	for (var k = 0; k<=5; k++)
		{
		for (var j = 0; j<=7; j++)
			{
			document.theSpreadsheet[9+k*9+j].value = theMatrix[0][vcount+k+1][hcount+j+1];
			}
		}
	}
updatehold = 1;
}

function reset() {
	updatehold = 0;
	hcount = 0;
	vcount = 0;
	// change the row and column headers back to 1 - 8
	for  (var i = 0; i<=5; i++)
		{
		document.theSpreadsheet[8+9*i].value = eval(i+1);
		document.theSpreadsheet[i].value = eval(i+1);
		}
	document.theSpreadsheet[6].value = eval(7);
	document.theSpreadsheet[7].value = eval(8);
	// change the entries
	for (var k = 0; k<=5; k++)
		{
		for (var j = 0; j<=7; j++)
			{
			document.theSpreadsheet[9+k*9+j].value = theMatrix[0][vcount+k+1][hcount+j+1];
			}
		}
	updatehold = 1;
}


// ******************update the i,j entry in the spreadsheet matrix when a change occurs

function update(ival,jval) {
if (updatehold == 1)
	{
	showdisplay = 0;
	theMatrix[0][vcount+ival][hcount+jval] = document.theSpreadsheet[8+9*(ival-1)+jval].value;
	}
}

// *********************doit either saves as, displays, or clears the spread sheet

function clearit() {
	theMatrix[0] = new makeArray2(maxRows,maxCols);
	reset();
}

function doit() {
	if (document.theSpreadsheet.stuff.value == 1) //save the selected matrix and update rows[k] and cols[k]
		{
		var checkrational = 1;
		var k = eval(document.theSpreadsheet.vars.value);
		rows[k] = 0;
		cols[k] = 0;
		for (var i=1; i<=20; i++)
			{
			for (var j=1; j<=20; j++)
				{
				if (stripSpaces(theMatrix[0][i][j]) != "")  //check if we are at a larger row or column location than the current values in rows[k] and cols[k]
					{
					if (isrational(theMatrix[0][i][j]) == "NOT") {checkrational = 0;}
					if (i > rows[k]) {rows[k] = i;}
					if (j > cols[k]) {cols[k] = j;}
					}
				}
			}
		if (checkrational == 1)
			{
			// Now fill in the zeros
			for (var i=1; i<=20; i++)
				{
				for (var j=1; j<=20; j++)
					{
					if ((i > rows[k]) || (j > cols[k])) {theMatrix[k][i][j] = "";}
					else
						{
						if (stripSpaces(theMatrix[0][i][j]) == "") 
							{
							theMatrix[k][i][j] = "0";
							}
						else
							{
							theMatrix[k][i][j] = isrational(theMatrix[0][i][j]);
							}
						}
					}
				}
				loghistory = loghistory + matnames[k] + " is now\n";
				loghistory = loghistory + writematrix(k);
				doshowlog();
				showdisplay = 1;
			}
		else alert('The entries are not all rational numbers.');
		}
	if (document.theSpreadsheet.stuff.value == 2) //display the selected matrix
		{
		var k = eval(document.theSpreadsheet.vars.value);
		for (var i=1; i<=20; i++)
			{
			for (var j=1; j<=20; j++)
				{
				theMatrix[0][i][j]=theMatrix[k][i][j];
				}
			}
		reset();
		loghistory = loghistory + matnames[k] + " was displayed as\n";
		loghistory = loghistory + writematrix(k);
		doshowlog();
		showdisplay = 1;
		}

}

function fill() { // fills in zeros in the display matrix and determines the dimension.
	rows[0] = 0;
	cols[0] = 0;
	for (var i=1; i<=20; i++)
		{
		for (var j=1; j<=20; j++)
			{
			if (theMatrix[0][i][j] != "")  //check if we are at a larger row or column location than the current values in rows[k] and cols[k]
				{
				if (i > rows[0]) {rows[0] = i;}
				if (j > cols[0]) {cols[0] = j;}
				}
			}
		}
	// Now fill in the zeros
	for (var i=1; i<=rows[0]; i++)
		{
		for (var j=1; j<=cols[0]; j++)
			{
			if (theMatrix[0][i][j] == "") 
				{
				theMatrix[0][i][j] = "0";
				}
			}
		}
	// finally, redraw the display matrix with the zeros inserted.
	reset();
}

function isrational(b) { // determines if the entry is a fraction or a decimal, and converts to a fraction in lowest terms. No repeated decimals are assumed.
	var a = stripSpaces(b);
	var junk = ""; // we will return the stuff here
	var pm = 1;
	var d = 1;
	if (a.indexOf("/") != -1)
		{ // there is a / in the string
		var frac = a.split("/");
		if ((parseInt(frac[0]) != frac[0]) || (parseInt(frac[1]) != frac[1]) || (frac[0].length == 0) || (frac[1].length == 0) || (frac[1] <= 0))
			{  // it is not a fraction with a positive denominator
			junk = "NOT";
			}
		else
			{  // it is a fraction with a positive denominator
			if (frac[0] == 0) {junk = "0";}
			else
				{
					if (frac[0] < 0) {d = gcd(-frac[0],frac[1]);}
					else {d = gcd(frac[0],frac[1]);}
					if (frac[1]/d == 1) {junk = frac[0]/d + "";} else {junk = frac[0]/d + "/" + frac[1]/d;}
				}
			}
		}
	else
		{ // there is not a / in the string
		if (isNaN(a*1) || a.length == 0)
			{ // a is not a number
			junk = "NOT";	
			}
		else
			{ // a is a number. There must be a . present.
			if (parseInt(a) == a)
				{ //decimal must have been at the end or there was nothing but 0 after the decimal
				junk = parseInt(a) + "";
				}
			else
				{ // the decimal is not at the end
				var real = a.split(".");
				if (a < 0) {pm = -1;}
				if (Math.abs(a) < 1) {real[0] = 0;}
				var top = Math.abs(real[0])*Math.pow(10,real[1].length)+eval(real[1]);
				var bottom = Math.pow(10,real[1].length);
				d = gcd(top,bottom);
				junk = pm*top/d + "/" + bottom/d;
				}
			}
		}
	return (junk);
}


function numer(a) { // gives the denominator of a bonifide rational number
	var junk = "";
	if (a.indexOf("/") == -1) 
		{
		junk = a;
		}
	else 
		{
		var frac = a.split("/"); 
		junk = frac[0];
		}
	return junk;
}

function denom(a) { // gives the denominator of a bonifide rational number
	var junk = "";
	if (a.indexOf("/") == -1) 
		{
		junk = "1";
		}
	else 
		{
		var frac = a.split("/"); 
		junk = frac[1];
		}
	return junk;
}

function dorowopadd() {
	fill();
	var a = document.theSpreadsheet.alpha.value;
	var b = document.theSpreadsheet.beta.value;
	if ((isrational(a) == "NOT") || (isrational(b) == "NOT"))
		{
		// either alpha or beta is not rational
		alert('This operation requires rational numbers for values.');
		} 
	else
		{
		// alpha and beta are rational. Check whether the selected rows are allowed and have rational values.
		var i1 = document.theSpreadsheet.row1.value;
		var i2 = document.theSpreadsheet.row2.value;
		if ((i1 > rows[0]) || (i2 > rows[0]) || (i1 == i2))
			{
			// they selected an illegal row
			alert('Illegal row selection. The rows cannot be equal and they must include at least one nonempty value.');
			}
		else
			{
			// check if the entries in the selected rows are rational
			var ok = 1;
			var i = i1;
			for (var j = 1; j <= cols[0]; j++)
				{
				if (isrational(theMatrix[0][i][j]) == "NOT") { ok = "Some entries are not rational.";}
				}
			var i = i2;
			for (var j = 1; j <= cols[0]; j++)
				{
				if (isrational(theMatrix[0][i][j]) == "NOT") { od = "Some entries are not rational.";}
				}
			if (ok != 1) { alert(ok);}
			else 
				{
				if (showdisplay == 0)
					{
					loghistory = loghistory + "The display matrix is\n";
					loghistory = loghistory + writematrix(0); 
					}
				addrow(a,i1,b,i2);
				loghistory = loghistory + "("+a+")R"+i1+" + ("+b+")R"+i2+" -> R"+i2+" gives\n";
				loghistory = loghistory + writematrix(0);
				doshowlog();
				}
			}
		}
}

function dorowopmul() {
	fill();
	var a = document.theSpreadsheet.gamma.value;
	if (isrational(a) == "NOT")
		{
		alert('The operation requires rational numbers for values.')
		}
	else
		{
		var ok = 1;
		var i = document.theSpreadsheet.row3.value;
		for (var j = 1; j <= cols[0]; j++)
				{
				if (isrational(theMatrix[0][i][j]) == "NOT") { ok = "Some row entries are not rational.";}
				}
		if (ok != 1) {alert(ok);}
		else 
			{
			if (showdisplay == 0)
				{
				loghistory = loghistory + "The display matrix is\n";
				loghistory = loghistory + writematrix(0); 
				}
			mulrow(a,i);
			loghistory = loghistory + "("+a+")R"+i+" -> R"+i+" gives\n";
			loghistory = loghistory + writematrix(0);
			doshowlog();
			}
		}
}

function dorowopswap() {
	fill();
	var i1 = document.theSpreadsheet.row4.value;
	var i2 = document.theSpreadsheet.row5.value;
	if ((i1 > rows[0]) || (i2 > rows[0]))
		{
		alert('Illegal row value(s).');
		}
	else
		{
		if (showdisplay == 0)
			{
			loghistory = loghistory + "The display matrix is\n";
			loghistory = loghistory + writematrix(0); 
			}
		swaprow(i1,i2);
		loghistory = loghistory + "R"+i1+" <-> R"+i2+" gives\n";
		loghistory = loghistory + writematrix(0);
		doshowlog();
		}
}

function addrow(a,r1,b,r2) {
	// r2 is replaced by a*r1+b*r2
	// we already know everything is legal, or this function would not get called.
	var i1 = r1;
	var i2 = r2;
	var y1 = 0;
	var y2 = 0;
	var x1 = 0;
	var x2 = 0;
	for (var j=1; j<=cols[0]; j++)
		{
		y1 = eval(numer(a))*eval(numer(theMatrix[0][i1][j]));
		x1 = eval(denom(a))*eval(denom(theMatrix[0][i1][j]));
		y2 = eval(numer(b))*eval(numer(theMatrix[0][i2][j]));
		x2 = eval(denom(b))*eval(denom(theMatrix[0][i2][j]));
		theMatrix[0][i2][j] = isrational(x1*y2+x2*y1 + "/" + x1*x2);
		}
	reset();
}

function mulrow(a,r1) {
	// replaces r1 with a*r1. No checking is done.
	var i = r1;
	for (var j=1; j<=cols[0]; j++)
		{
		theMatrix[0][i][j] = isrational(numer(a)*numer(theMatrix[0][i][j]) + "/" +denom(a)*denom(theMatrix[0][i][j]));
		}
	reset();
}

function swaprow(i1,i2) {
	// swaps i1 with i2
	var a = ""; //for holding values
	for (var j=1; j<=cols[0]; j++)
		{
		a = theMatrix[0][i2][j];
		theMatrix[0][i2][j] = theMatrix[0][i1][j];
		theMatrix[0][i1][j] = a;
		}
	reset();
}

function multiply(a,b) { //multiplies the rational numbers a and b, assuming the values are truly rational
	var y = eval(numer(a)*numer(b));
	var x = eval(denom(a)*denom(b));
	// create the reduced fraction for y/x
	return isrational(y + "/" + x);
}

function add(a,b) { //adds the rational numbers and b, assuming the values are truly rational
	return isrational(eval(denom(b)*numer(a)+denom(a)*numer(b)) + "/" + eval(denom(a)*denom(b)));
}

function invert(a) { // is only called when a is nonzero. In this case, it returns 1/a in reduced form.
	if (a == "0") {alert('I can only invert nonzero numbers.');}
	else 
		{
		if (numer(a) > 0)
			{
			return isrational(denom(a)+"/"+numer(a));
			}
		else
			{
			return isrational(multiply("-1",denom(a))+"/"+multiply("-1",numer(a)));
			}
		}
}

function cloneit() {
	document.theSpreadsheet.row2clone.value = document.theSpreadsheet.row2.value;
}


function gcd(a,b)  //finds gcd assuming a>=b are positive integers
	{
	while (true) //loop until a or b is zero
		{
		a=a%b;
		if (a==0) 
			{
			return b;
			}
		b=b%a;
		if (b==0) 
			{
			return a;
			}
		}
	return b;
	}


function stripSpaces (InString)  {
	var OutString = "";
	var TempChar = "";
	for (var Count=0; Count < InString.length; Count++)  {
		TempChar=InString.substring (Count, Count+1);
		if (TempChar!=" ")
			OutString=OutString+TempChar;
		}
	return (OutString);
	}


function makeArray3 (X,Y,Z)
	{
	var count;
	this.length = X+1;
	for (var count = 0; count <= X; 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; count++)
		// to allow starting at 1
		this[count] = new makeArray(Y);
	} // makeArray2

function makeArray (Y)
	{
	var count;
	this.length = Y+1;
	for (var count = 0; count <= Y; count++)
		this[count] = "";
	} // makeArray

// The following code is due to David Binner, david@akiti.ca, and we are deeply grateful to his contribution.

var N = 5; //Global variable, dimension of matrix.

function cdivA(ar, ai, br, bi, A, in1, in2, in3){
// Division routine for dividing one complex number into another:
// This routine does (ar + ai)/(br + bi) and returns the results in the specified
// elements of the A matrix.

 var s, ars, ais, brs, bis;

 s = Math.abs(br) + Math.abs(bi);
 ars = ar/s;
 ais = ai/s;
 brs = br/s;
 bis = bi/s;
 s = brs*brs + bis*bis;
 A[in1][in2] = (ars*brs + ais*bis)/s;
 A[in1][in3] = (-(ars*bis) + ais*brs)/s;
 return;
} // End cdivA

function hqr2(A, B, low, igh, wi, wr, ierr){
/* Computes the eigenvalues and eigenvectors of a real upper-Hessenberg Matrix using the QR method. */

var norm = 0.0, p, q, ra, s, sa, t = 0.0, tst1, tst2, vi, vr, w, x, y, zz;
var k = 0, l, m, mp2, en = igh, incrFlag = 1, its, itn = 30*N, enm2, na, notlas;

for (var i = 0; i < N; i++){ // Store eigenvalues already isolated and compute matrix norm.
 for (var j = k; j < N; j++)
  norm += Math.abs(A[i][j]);
 k = i;
 if ((i < low) || (i > igh)){
  wi[i] = 0.0;
  wr[i] = A[i][i];
 } //End if (i < low or i > igh)
}  // End for i

// Search next eigenvalues
while (en >= low){

 if (incrFlag) { //Skip this part if incrFlag is set to 0 at very end of while loop
  its = 0;
  na = en - 1;
  enm2 = na - 1;
 } //End if (incrFlag)
 else
  incrFlag = 1;
 
  /*Look for single small sub-diagonal element for l = en step -1 until low */

 for (var i = low; i <= en; i++){
  l = en + low - i;
  if (l == low)
   break;
  s = Math.abs(A[l - 1][l - 1]) + Math.abs(A[l][l]);
  if (s == 0.0)
   s = norm;
  tst1 = s;
  tst2 = tst1 + Math.abs(A[l][l - 1]);
  if (tst2 == tst1)
   break;
 } //End for i

 x = A[en][en];

 if (l == en){  //One root found
  wr[en] = A[en][en] = x + t;
  wi[en] = 0.0;
  en--;
  continue;
 } //End if (l == en)

 y = A[na][na];
 w = A[en][na]*A[na][en];
 
 if (l == na){  //Two roots found
  p = (-x + y)/2;
  q = p*p + w;
  zz = Math.sqrt(Math.abs(q));
  x = A[en][en] = x + t;
  A[na][na] = y + t;
  if (q >= 0.0){//Real Pair
   zz = ((p < 0.0) ? (-zz + p) : (p + zz));
   wr[en] = wr[na] = x + zz;
   if (zz != 0.0)
    wr[en] = -(w/zz) + x;
   wi[en] = wi[na] = 0.0;
   x = A[en][na];
   s = Math.abs(x) + Math.abs(zz);
   p = x/s;
   q = zz/s;
   r = Math.sqrt(p*p + q*q);
   p /= r;
   q /= r;
   for (var j = na; j < N; j++){ //Row modification
    zz = A[na][j];
    A[na][j] = q*zz + p*A[en][j];
    A[en][j] = -(p*zz) + q*A[en][j];
   }//End for j
   for (var j = 0; j <= en; j++){ // Column modification
    zz = A[j][na];
    A[j][na] = q*zz + p*A[j][en];
    A[j][en] = -(p*zz) + q*A[j][en];
   }//End for j
   for (var j = low; j <= igh; j++){//Accumulate transformations
    zz = B[j][na];
    B[j][na] = q*zz + p*B[j][en];
    B[j][en] = -(p*zz) + q*B[j][en];
   }//End for j
  } //End if (q >= 0.0)
  else {//else q < 0.0
   wr[en] = wr[na] = x + p;
   wi[na] = zz;
   wi[en] = -zz;
  } //End else
  en--;
  en--;
  continue;
 }//End if (l == na)
 
 if (itn == 0){ //Set error; all eigenvalues have not converged after 30 iterations.
  ierr = en + 1;
  return;
 }//End if (itn == 0)

 if ((its == 10) || (its == 20)){ //Form exceptional shift
  t += x;
  for (var i = low; i <= en; i++)
   A[i][i] += -x;
  s = Math.abs(A[en][na]) + Math.abs(A[na][enm2]);
  y = x = 0.75*s;
  w = -0.4375*s*s;
 } //End if (its equals 10 or 20)

 its++;
 itn--;

/*Look for two consecutive small sub-diagonal elements. Do m = en - 2 to l in increments of -1 */

 for (var m = enm2; m >= l; m--){
  zz = A[m][m];
  r = -zz + x;
  s = -zz + y;
  p = (-w + r*s)/A[m + 1][m] + A[m][m + 1];
  q = -(zz + r + s) + A[m + 1][m + 1] ;
  r = A[m + 2][m + 1];
  s = Math.abs(p) + Math.abs(q) + Math.abs(r);
  p /= s;
  q /= s;
  r /= s;
  if (m == l)
   break;
  tst1 = Math.abs(p) * (Math.abs(A[m - 1][m - 1]) + Math.abs(zz) + Math.abs(A[m + 1][m + 1]));
  tst2 = tst1 + Math.abs(A[m][m - 1]) * (Math.abs(q) + Math.abs(r));
  if (tst1 == tst2)
   break;
 }//End for i

 mp2 = m + 2;
 
 for (var i = mp2; i <= en; i++){
  A[i][i - 2] = 0.0;
  if (i == mp2)
   continue;
  A[i][i - 3] = 0.0;
 }//End for i
 
 /* Double qr step involving rows l to en and columns m to en. */

 for (var i = m; i <= na; i++){
  notlas = ((i != na) ? 1 : 0);
  if (i != m){
   p = A[i][i - 1];
   q = A[i + 1][i - 1];
   r = ((notlas) ? A[i + 2][i - 1] : 0.0);
   x = Math.abs(p) + Math.abs(q) + Math.abs(r);
   if (x == 0.0)      //Drop through rest of for i loop
    continue;
   p /= x;
   q /= x;
   r /= x;
  } //End if (i != m)

  s = Math.sqrt(p*p + q*q + r*r);
  if (p < 0.0)
   s = -s;

  if (i != m)
   A[i][i - 1] = -(s*x);
  else {
   if (l != m)
    A[i][i - 1] = -A[i][i - 1];
  }

  p += s;
  x = p/s;
  y = q/s;
  zz = r/s;
  q /= p;
  r /= p;
  k = ((i + 3 < en) ? i + 3 : en);

  if (notlas){ //Do row modification
   for (var j = i; j < N; j++) {
    p = A[i][j] + q*A[i + 1][j] + r*A[i + 2][j];
    A[i][j] += -(p*x);
    A[i + 1][j] += -(p*y);
    A[i + 2][j] += -(p*zz);
   }//End for j

   for (var j = 0; j <= k; j++) {//Do column modification
    p = x*A[j][i] + y*A[j][i + 1] + zz*A[j][i + 2];
    A[j][i] += -p;
    A[j][i + 1] += -(p*q);
    A[j][i + 2] += -(p*r);
   }//End for j
   
   for (var j = low; j <= igh; j++) {//Accumulate transformations
    p = x*B[j][i] + y*B[j][i + 1] + zz*B[j][i + 2];
    B[j][i] += -p;
    B[j][i + 1] += -(p*q);
    B[j][i + 2] += -(p*r);
   } // End for j
  }//End if notlas

  else {
   for (var j = i; j < N; j++) {//Row modification
    p = A[i][j] + q*A[i + 1][j];
    A[i][j] += -(p*x);
    A[i + 1][j] += -(p*y);
   }//End for j

   for (var j = 0; j <= k; j++){//Column modification
    p = x*A[j][i] + y*A[j][i +1];
    A[j][i] += -p;
    A[j][i + 1] += -(p*q);
   }//End for j

   for (var j = low; j <= igh; j++){//Accumulate transformations
    p = x*B[j][i] + y*B[j][i +1];
    B[j][i] += -p;
    B[j][i + 1] += -(p*q);
   }//End for j

  } //End else if notlas
 }//End for i
 incrFlag = 0;
}//End while (en >= low)

if (norm == 0.0)
 return;

//Step from (N - 1) to 0 in steps of -1

for (var en = (N - 1); en >= 0; en--){
 p = wr[en];
 q = wi[en];
 na = en - 1;

 if (q > 0.0)
  continue;

 if (q == 0.0){//Real vector
  m = en;
  A[en][en] = 1.0;

  for (var j = na; j >= 0; j--){
   w = -p + A[j][j];
   r = 0.0;
   for (var ii = m; ii <= en; ii++)
    r += A[j][ii]*A[ii][en];

   if (wi[j] < 0.0){
    zz = w;
    s = r;
   }//End wi[j] < 0.0

   else {//wi[j] >= 0.0
    m = j;
    if (wi[j] == 0.0){
     t = w;
     if (t == 0.0){
      t = tst1 = norm;
      do {
       t *= 0.01;
       tst2 = norm + t;
      } while (tst2 > tst1);
     } //End if t == 0.0
     A[j][en] = -(r/t);
    }//End if wi[j] == 0.0

    else { //wi[j] > 0.0; Solve real equations
     x = A[j][j + 1];
     y = A[j + 1][j];
     q = (-p + wr[j])*(-p + wr[j]) + wi[j]*wi[j];
     A[j][en] = t = (-(zz*r) + x*s)/q;
     A[j + 1][en] = ((Math.abs(x) > Math.abs(zz)) ? -(r + w*t)/x : -(s + y*t)/zz);
    }//End  else wi[j] > 0.0

    // Overflow control
    t = Math.abs(A[j][en]);
    if (t == 0.0)
     continue; //go up to top of for j loop
    tst1 = t;
    tst2 = tst1 + 1.0/tst1;
    if (tst2 > tst1)
     continue; //go up to top of for j loop
    for (var ii = j; ii <= en; ii++)
     A[ii][en] /= t;

   }//End else wi[j] >= 0.0

  }//End for j
  
 }      //End q == 0.0

 else {//else q < 0.0, complex vector
 //Last vector component chosen imaginary so that eigenvector matrix is triangular
 m = na;

 if (Math.abs(A[en][na]) <= Math.abs(A[na][en]))
  cdivA(0.0, -A[na][en], -p + A[na][na], q, A, na, na, en);
 else {
  A[na][na] = q/A[en][na];
  A[na][en] = -(-p + A[en][en])/A[en][na];
 } //End else (Math.abs(A[en][na] > Math.abs(A[na][en])

 A[en][na] = 0.0;
 A[en][en] = 1.0;

 for (var j = (na - 1); j >= 0; j--) {
  w = -p + A[j][j];
  sa = ra = 0.0;
  
  for (var ii = m; ii <= en; ii++) {
   ra += A[j][ii]*A[ii][na];
   sa += A[j][ii]*A[ii][en];
  } //End for ii

  if (wi[j] < 0.0){
   zz = w;
   r = ra;
   s = sa;
   continue;
  } //End if (wi[j] < 0.0)

  //else wi[j] >= 0.0
  m = j;
  if (wi[j] == 0.0)
   cdivA(-ra, -sa, w, q, A, j, na, en);
  else {//wi[j] > 0.0; solve complex equations
   x = A[j][j + 1];
   y = A[j + 1][j];
   vr = -(q*q) + (-p + wr[j])*(-p + wr[j]) + wi[j]*wi[j];
   vi = (-p + wr[j])*2.0*q;
   if ((vr == 0.0) && (vi == 0.0)){
    tst1 = norm*(Math.abs(w) + Math.abs(q) + Math.abs(x) + Math.abs(y) + Math.abs(zz));
    vr = tst1;
    do {
     vr *= 0.01;
     tst2 = tst1 + vr;
    } while (tst2 > tst1);
   } //End if vr and vi == 0.0
   cdivA(-(zz*ra) + x*r + q*sa, -(zz*sa + q*ra) + x*s, vr, vi, A, j, na, en);

   if (Math.abs(x) > (Math.abs(zz) + Math.abs(q))){
    A[j + 1][na] = (-(ra + w*A[j][na]) + q*A[j][en])/x;
    A[j + 1][en] = -(sa + w*A[j][en] + q*A[j][na])/x;
   }//End if
   else
    cdivA(-(r + y*A[j][na]), -(s + y*A[j][en]), zz, q, A, j + 1, na, en);

  }//End else wi[j] > 0.0
    
  t = ((Math.abs(A[j][na]) >= Math.abs(A[j][en])) ? Math.abs(A[j][na]) : Math.abs(A[j][en]));

  if (t == 0.0)
   continue; // go to top of for j loop

  tst1 = t;
  tst2 = tst1 + 1.0/tst1;
  if (tst2 > tst1)
   continue; //go to top of for j loop
    
  for (var ii = j; ii <= en; ii++){
   A[ii][na] /= t;
   A[ii][en] /= t;
  } //End for ii loop

 } // End for j
  
 }//End else q < 0.0
 
}//End for en

//End back substitution. Vectors of isolated roots.

for (var i = 0; i < N; i++){
 if ((i < low) || (i > igh)) {
  for (var j = i; j < N; j++)
   B[i][j] = A[i][j];
 }//End if i
}//End for i

// Multiply by transformation matrix to give vectors of original full matrix.

//Step from (N - 1) to low in steps of -1.

for (var i = (N - 1); i >= low; i--){

 m = ((i < igh) ? i : igh);
 
 for (var ii = low; ii <= igh; ii++){
  zz = 0.0;
  for (var jj = low; jj <= m; jj++)
   zz += B[ii][jj]*A[jj][i];
  B[ii][i] = zz;
 }//End for ii
}//End of for i loop

return;
} //End of function hqr2

function norVecC(Z, wi){// Normalizes the eigenvectors

// This subroutine is based on the LINPACK routine SNRM2, written 25 October 1982, modified
// on 14 October 1993 by Sven Hammarling of Nag Ltd.
// I have further modified it for use in this Javascript routine, for use with a column
// of an array rather than a column vector.
//
// Z - eigenvector Matrix
// wi - eigenvalue vector

 var scale, ssq, absxi, dummy, norm;

 for (var j = 0; j < N; j++){ //Go through the columns of the vector array 
  scale = 0.0;
  ssq = 1.0;

  for (var i = 0; i < N; i++){
   if (Z[i][j] != 0){
    absxi = Math.abs(Z[i][j]);
    dummy = scale/absxi;
    if (scale < absxi){
     ssq = 1.0 + ssq*dummy*dummy;
     scale = absxi;
    }//End if (scale < absxi)
    else
     ssq += 1.0/dummy/dummy;
   }//End if (Z[i][j] != 0)
  } //End for i

  if (wi[j] != 0){// If complex eigenvalue, take into account imaginary part of eigenvector
   for (var i = 0; i < N; i++){
    if (Z[i][j + 1] != 0){
     absxi = Math.abs(Z[i][j + 1]);
     dummy = scale/absxi;
     if (scale < absxi){
      ssq = 1.0 + ssq*dummy*dummy;
      scale = absxi;
     }//End if (scale < absxi)
     else
      ssq += 1.0/dummy/dummy;
     }//End if (Z[i][j + 1] != 0)
   } //End for i
  }//End if (wi[j] != 0)

  norm = scale*Math.sqrt(ssq); //This is the norm of the (possibly complex) vector

  for (var i = 0; i < N; i++)
   Z[i][j] /= norm;

  if (wi[j] != 0){// If complex eigenvalue, also scale imaginary part of eigenvector
   j++;
   for (var i = 0; i < N; i++)
   Z[i][j] /= norm;
  }//End if (wi[j] != 0)

 }// End for j
 
 return;
} // End norVecC

function EigSolve(ii,jj){

// Approximates the eigenvalues of theMatrix[ii] when jj<>1, and computes the eigenvectors when jj=1. 
// This routine is only called if the theMatrix[ii] is nonempty and square. 

N = rows[ii]; // Setting the new value of the global variable N based upon the size of theMatrix[ii].

var A = new Array(N);
var B = new Array(N);
for (var i = 0; i < N; i++){
 A[i] = new Array(N);
 B[i] = new Array(N);
}

//Input data from theMatrix[ii]
for (var i = 0; i < N; i++) {
 for (var j = 0; j < N; j++) {
  A[i][j] = eval(theMatrix[ii][i+1][j+1]);
 }// End for j loop
}// End for i loop

var wi = new Array(N);
var wr = new Array(N);

/* Balance the matrix to improve accuracy of eigenvalues. Introduces no rounding errors, since it scales A by powers of the radix.
*/

var scale = new Array(N);    //Contains information about transformations.
var trace = new Array(N);    //Records row and column interchanges
var radix = 2;               //Base of machine floating point representation.
var c, f, g, r, s, b2 = radix*radix;
var ierr = -1, igh, low, k = 0, l = N - 1, noconv;

//Search through rows, isolating eigenvalues and pushing them down.

noconv = l;

while (noconv >= 0){
 r = 0;

 for (var j = 0; j <= l; j++) {
  if (j == noconv) continue;
  if (A[noconv][j] != 0.0){
   r = 1;
   break;
  }
 } //End for j

 if (r == 0){
  scale[l] = noconv;

  if (noconv != l){
   for (var i = 0; i <= l; i++){
    f = A[i][noconv];
    A[i][noconv] = A[i][l];
    A[i][l] = f;
   }//End for i
   for (var i = 0; i < N; i++){
    f = A[noconv][i];
    A[noconv][i] = A[l][i];
    A[l][i] = f;
   }//End for i
  }//End if (noconv != l)

  if (l == 0)
   break;  //break out of while loop

  noconv = --l;

 }//End if (r == 0)

 else //else (r != 0)
  noconv--;

}//End while noconv

if (l > 0) {  //Search through columns, isolating eigenvalues and pushing them left.

 noconv = 0;

 while (noconv <= l){  
  c = 0;

  for (var i = k; i <= l; i++){
   if (i == noconv) continue;
   if (A[i][noconv] != 0.0){
    c = 1;
    break;
   }
  }//End for i

  if (c == 0){
   scale[k] = noconv;

   if (noconv != k){
    for (var i = 0; i <= l; i++){
     f = A[i][noconv];
     A[i][noconv] = A[i][k];
     A[i][k] = f;
    }//End for i
    for (var i = k; i < N; i++){
     f = A[noconv][i];
     A[noconv][i] = A[k][i];
     A[k][i] = f;
    }//End for i

   }//End if (noconv != k)

   noconv = ++k;
  }//End if (c == 0)

  else  //else (c != 0)
   noconv++;

 }//End while noconv

 //Balance the submatrix in rows k through l.

 for (var i = k; i <= l; i++)
  scale[i] = 1.0;

 //Iterative loop for norm reduction
 do {
  noconv = 0;
  for (var i = k; i <= l; i++) {
   c = r = 0.0;
   for (var j = k; j <= l; j++){
    if (j == i) continue;
    c += Math.abs(A[j][i]);
    r += Math.abs(A[i][j]);
   } // End for j
   if ((c == 0.0) || (r == 0.0)) continue;   //guard against zero c or r due to underflow
   g = r/radix;
   f = 1.0;
   s = c + r;
   while (c < g) {
    f *= radix;
    c *= b2;
   } // End while (c < g)
   g = r*radix;
   while (c >= g) {
    f /= radix;
    c /= b2;
   } // End while (c >= g)

   //Now balance
   if ((c + r)/f < 0.95*s) {
    g = 1.0/f;
    scale[i] *= f;
    noconv = 1;
    for (var j = k; j < N; j++)
     A[i][j] *= g;
    for (var j = 0; j <= l; j++)
     A[j][i] *= f;
   } //End if ((c + r)/f < 0.95*s)
  } // End for i
 } while (noconv);  // End of do-while loop.

} //End if (l > 0)

low = k;
igh = l;

//End of balanc
 
/* Now reduce the real general Matrix to upper-Hessenberg form using stabilized elementary similarity transformations. */

for (var i = (low + 1); i < igh; i++){
 k = i;
 c = 0.0;

 for (var j = i; j <= igh; j++){
  if (Math.abs(A[j][i - 1]) > Math.abs(c)){
   c = A[j][i - 1];
   k = j;
  }//End if
 }//End for j

 trace[i] = k;

 if (k != i){
  for (var j = (i - 1); j < N; j++){
   r = A[k][j];
   A[k][j] = A[i][j];
   A[i][j] = r;
  }//End for j

  for (var j = 0; j <= igh; j++){
   r = A[j][k];
   A[j][k] = A[j][i];
   A[j][i] = r;
  }//End for j
 }//End if (k != i)

 if (c != 0.0){
  for (var m = (i + 1); m <= igh; m++){
   r = A[m][i - 1];

   if (r != 0.0){
    r = A[m][i - 1] = r/c;
    for (var j = i; j < N; j++)
     A[m][j] += -(r*A[i][j]);
    for (var j = 0; j <= igh; j++)
     A[j][i] += r*A[j][m];
   }//End if (r != 0.0)
  }//End for m
 }//End if (c != 0)
}  //End for i.

/* Accumulate the stabilized elementary similarity transformations used in the reduction of A to upper-Hessenberg form. Introduces no rounding errors since it only transfers the multipliers used in the reduction process into the eigenvector matrix. */

for (var i = 0; i < N; i++){ //Initialize B to the identity Matrix.
 for (var j = 0; j < N; j++)
  B[i][j] = 0.0;
 B[i][i] = 1.0;
} //End for i

for (var i = (igh - 1); i >= (low + 1); i--){
 k = trace[i];
 for (var j = (i + 1); j <= igh; j++)
  B[j][i] = A[j][i - 1];

 if (i == k)
  continue;

 for (var j = i; j <= igh; j++){
  B[i][j] = B[k][j];
  B[k][j] = 0.0;
 } //End for j

 B[k][i] = 1.0; 

} // End for i
 
hqr2(A, B, low, igh, wi, wr, ierr);

if (jj!=1)
	{
	// Time to write the eigenvalues to the text area and update the log.

	var logholder = "The eigenvalues of " + matnames[ii] + " are\n";

	for (var i=1; i<=N; i++)
		{
		logholder = logholder + wr[i-1] + " + " + wi[i-1] + " i\n"
		}
	if (ierr!=-1)
		{logholder = logholder + "There could be a problem with value(s) " + eval(ierr+1) + " to " + rows[ii] + ".\n";}
	loghistory = loghistory + logholder;
	doshowlog();
	}
else
	{
	// Time to write the eigenvalue and eigenvector pairs.
if (ierr == -1){

if (low != igh){
 for (var i = low; i <= igh; i++){
   s = scale[i];
   for (var j = 0; j < N; j++)
    B[i][j] *= s;
  }//End for i
 }//End if (low != igh)

 for (var i = (low - 1); i >= 0; i--){
  k = scale[i];
  if (k != i){
   for (var j = 0; j < N; j++){
    s = B[i][j];
    B[i][j] = B[k][j];
    B[k][j] = s;
   }//End for j
  }//End if k != i
 }//End for i

 for (var i = (igh + 1); i < N; i++){
  k = scale[i];
  if (k != i){
   for (var j = 0; j < N; j++){
    s = B[i][j];
    B[i][j] = B[k][j];
    B[k][j] = s;
   }//End for j
  }//End if k != i
 }//End for i

 norVecC(B, wi);  //Normalize the eigenvectors
 
}//End if ierr = -1

	var logholder = "The eigenvalues of the matrix " + matnames[ii] + " are\n";

	for (var i=1; i<=N; i++)
		{
		logholder = logholder + wr[i-1] + " + " + wi[i-1] + " i\n"
		}
	if (ierr!=-1)
		{logholder = logholder + "There could be a problem with value(s) " + eval(ierr+1) + " to " + rows[ii] + ", and the eigenvector matrix below.\n";}
	logholder = logholder + "The corresponding eigenvector matrix is\n";
	for (var i=0; i<N; i++)
		{
		for (var j=0; j<N; j++)
			{
			if (j<N-1) {logholder = logholder + B[i][j] + ",   ";}
			else {logholder = logholder + B[i][j] + ";\n";}
			}
		}
	logholder = logholder + "(Press the Info button above to interpret this matrix.)\n";
	loghistory = loghistory + logholder;
	doshowlog();


	}


return;
}  //End of EigSolve

// end of JavaScript from David Brinner-->

function veryready() {
document.theSpreadsheet.worklog.value = "The calculator is loaded.";
}
