e['function4'] = function () {
	//all functions are private unless included in return statement
// 'abstract' method implementations (for api)
	function getInterface() {
		var out = "<ul>";
		out += "<li><h1>Solving Systems of Linear Equations</h1></li>";
		// input #1 (onKeyPress need to calc height & width of matrix in validate input)
		out += "<li><span id='matrix1_size' style='float:right;'>" + e['function3'].getSizeHtml(3,4) + "</span><label for='matrix1'>Augmented Matrix</label></li>";
		out += "<li class='input'><textarea id='matrix1' rows='8' cols='40'>";
		out += "5 12 -18 32\n";
		out += "4 -10 4 65\n";
		out += "2 5 4 36";
		out += "</textarea></li>";

/* Works -
4 -1 0 0 -1 0 0 0 15
-1 4 -1 0 0 -1 0 0 20
0 -1 4 -1 0 0 -1 0 20
0 0 -1 4 0 0 0 -1 30
-1 0 0 0 4 -1 0 0 0
0 -1 0 0 -1 4 -1 0 5
0 0 -1 0 0 -1 4 -1 10
0 0 0 -1 0 0 -1 4 15

Does Not Work - (too many rows)
25 5 1 106.8
64 8 1 177.2
144 12 1 279.2
89 13 2 284
*/

		// submit button
		out += "<li>&nbsp;</li>";
		out += "<li class='input'><input id='submit' onclick=\"e['function4'].getHTML(this, document.getElementById('returnValue'))\" type='submit' value='Solve System'/></li>";
		out += "</ul>";
		return out;
	}

	function getHTML(caller, returnNode) {
		// grab input vals
		var matrixContainer1 = document.getElementById('matrix1');
		if (validateInput(matrixContainer1)) {
			var m1 = e['function3'].getMatrix(matrixContainer1);

			var returnM = rowReduce(m1.slice()); // slice() creates a copy of the array
			//var returnM = getCoefficientMatrix(m1);

			//format for output and create html
			var out = "<ul>";
			out += "<li><h1>Results</h1></li>";
			out += "<li><span class='right'>" + validateSystem(m1) + "</span><label for='results'>Values of X</label></li>";
			out += "<li class='output'><textarea id='results' rows='8' disabled='disabled' cols='40'>";

			for (i = 0; i < e['function3'].getRows(returnM); i++) {
				out +='x' + (i+1) + ' = ' + returnM[i][e['function3'].getCols(returnM)-1].toFixed(3) + '\n';
			}

//			out += e['function3'].getPrintMatrix(returnM);
			out += "</textarea></li>";
			out += "</ul>";
			returnNode.innerHTML = out;
		}
	}

	function validateInput(matrixContainer) {
		return e['function3'].validateInput(matrixContainer);
	}

// Program & Presentation Functions
/* Inheriting:
	function getMatrix(from)
	function getPrintMatrix(m)
	function getRows(m)
	function getCols(m)
	getSizeHtml(r, c)
*/
	function validateSystem(aMatrix) {
		var r_coeffs;
		var r_aug;

		r_coeffs = rank(getCoefficientMatrix(aMatrix));
		r_aug = rank(aMatrix);

		if (r_coeffs < r_aug) { // zero solutions (inconsistent)
			return "Inconsistent (c=" + r_coeffs + ", a=" + r_aug + ")";
		} else if (r_coeffs == e['function3'].getCols(aMatrix)-1) { // one solution
			return "One Solution (c=" + r_coeffs + ", a=" + r_aug + ")";
		} else { // many solutions
			return "Many Solutions (c=" + r_coeffs + ", a=" + r_aug + ")";
		}
	}

	function getSmallerMatrix(row, aMatrix) {
		var sMatrix;
		sMatrix = new Array(e['function3'].getRows(aMatrix)-1);
		for (var r = 0; r < e['function3'].getRows(aMatrix); r++) {
			if (r != row) {
				sMatrix[(r>row ? r-1 : r)] = new Array(e['function3'].getCols(aMatrix)-1);
				for (var c = 1; c < e['function3'].getCols(aMatrix); c++) { //copy row excluding col 1
					sMatrix[(r>row ? r-1 : r)][c-1] = parseFloat(aMatrix[r][c]);
				}
			}
		}
		return sMatrix;
	}

	function getCoefficientMatrix(aMatrix) {
		var sMatrix;
		sMatrix = new Array(e['function3'].getRows(aMatrix));
		for (var r = 0; r < e['function3'].getRows(aMatrix); r++) {
			sMatrix[r] = new Array(e['function3'].getCols(aMatrix)-1);
			for (var c = 0; c < e['function3'].getCols(aMatrix)-1; c++) { //copy row excluding col 1
				sMatrix[r][c] = parseFloat(aMatrix[r][c]);
			}
		}
		return sMatrix
	}

// Math Functions
	function rowReduce(aMatrix) {
		//pivot row 1, 2, 3, etc?
		for (var row = 0; row < e['function3'].getRows(aMatrix); row++) {
			//normalize [row] (set [row][row] to be 1)
			var divNum = aMatrix[row][row];
			for (var col = 0; col < e['function3'].getCols(aMatrix); col++) {
				aMatrix[row][col] = aMatrix[row][col] / divNum;
			}
			//normalize column [row] (set all other values in this col to be 0)
			for (var row2 = 0; row2 < e['function3'].getRows(aMatrix); row2++) {
				if (row2 != row) {
					var divNum = aMatrix[row2][row];
					for (var col = 0; col < e['function3'].getCols(aMatrix); col++) {
						aMatrix[row2][col] = aMatrix[row][col] * (-1 * divNum) + aMatrix[row2][col];
					}
				}
			}
		}
		return aMatrix;
	}

	function rank(aMatrix) {
		var rank = -1;
		//we need to find large square submatrixes and then do breath-first recursion
		if (e['function3'].getCols(aMatrix) == e['function3'].getRows(aMatrix)) { //has to be square for now
			if (det(aMatrix) != 0) {
				rank = e['function3'].getRows(aMatrix);
			}
		}
		return rank;
	}

	function det(aMatrix) { //uses the Cofactor Method, assumes matrix being passed in is square
		var d = 0;

		for (var r = 0; r < e['function3'].getRows(aMatrix); r++) {
			var smallerMatrix = getSmallerMatrix(r, aMatrix);

			var M = 0;
			if (e['function3'].getCols(smallerMatrix) > 2 || e['function3'].getRows(smallerMatrix) > 2) {
				M = det(smallerMatrix);
			} else {
				M = (smallerMatrix[0][0] * smallerMatrix[1][1]) - (smallerMatrix[0][1] * smallerMatrix[1][0]);
			}

			var Cij = Math.pow(-1, r + 1);
			Cij *= M;

			d += aMatrix[r][0] * Cij;
		}

		return d;
	}

// return public pointers to the private methods
	return {
		getInterface:getInterface,
		getHTML:getHTML,
		validateInput:validateInput
	}

}();

