<!-- Begin
/*#############################################

 File: $File: //depot/EVPnet/web/evpnet.com/script/quizgen.js $
 Revision: $Revision: #2 $
 Modified: $DateTime: 2007/01/26 18:45:51 $

 Misc functions

 Code When          Who               What
 ---- ------------- ----------------- -------------------------------------
 EP01 24-Apr-2003   edward@evpnet.com Initial version

#############################################*/

// QG prefix stands for Quiz Generator

//******************** BUSINESS LOGIC ***********************//
//######### QG_clsData Class to hold two operands and a result ##########
function QG_clsData(in_op1, in_op2, in_result) {
	this.op1 = in_op1;
	this.op2 = in_op2;
	this.result = in_result;
}

//######### QG_clsOperands Class to generate operands ##########
//### Generates a pair of numeric operands according to input conditions
function QG_clsOperands(in_Operator, in_NoNegativeRes, in_MIN, in_MAX) {
	// in_Operator is one of the following: + - * /
	// in_NoNegativeRes defines if the result of subtraction can be negative
	// Operands should be between in_MIN and in_MAX
	
	this.operator = in_Operator;
	this.NoNegativeRes = in_NoNegativeRes;
	this.MIN = parseInt(in_MIN);
	this.MAX = parseInt(in_MAX);
	this.data = new QG_clsData(Number.NaN, Number.NaN, Number.NaN); // initially Not a Number
	
	// public methods
	this.generate = QG_clsOperands_generate;
	this.asString = QG_clsOperands_asString;
	
	// "private" methods
	this._generateOperand = QG_clsOperands_generateOperand;
	this._evalResult = QG_clsOperands_evalResult;
}

// Evaluates the result
function QG_clsOperands_evalResult() {
	eval("this.data.result = Math.round(this.data.op1 " + this.operator + " this.data.op2, 2)");
}

// Generates operands op1 and op2
function QG_clsOperands_generate() {
	
	this.data.op1 = this._generateOperand();
	this.data.op2 = this._generateOperand();

	this._evalResult();
	
	if ( isNaN(this.data.result) ) this.data.result = "error";
	
	// check No Negative subtraction result, if required
	if ( this.operator == '-' && this.NoNegativeRes ) {
		if ( ! isNaN(this.data.result) && this.data.result < 0) {
			// swap operands
			var x = this.data.op1;
			this.data.op1 = this.data.op2;
			this.data.op2 = x;
			this._evalResult();
		}
	}
}

function QG_clsOperands_generateOperand() {
	var res;
	
	with (Math) {
		if ( this.MIN >= 0 && this.MAX >= 0) {
			res = floor(getRandom() * 
				(this.MAX - this.MIN + (this.MIN == 0 ? 0 : 1)) + this.MIN);
		}
		else	
		if ( this.MIN <= 0 && this.MAX <= 0) {
			res = -floor(getRandom() * 
					(abs(this.MAX) - abs(this.MIN) + (this.MIN == 0 ? 0 : 1)) + 
					abs(this.MIN));
		}
		else
		if ( this.MIN <= 0 && this.MAX >= 0) {
			// Do a 50/50 for negative and positive intervals
			if ( random() < 0.5 ) { // positive
				res = round(getRandom() * this.MAX);
			}
			else { // negative
				res = -round(getRandom() * abs(this.MIN));
			}
		}
	} // with
	
	if ( res > this.MAX ) res = this.MAX;
	if ( res < this.MIN ) res = this.MIN;
	
	return res;
}

function QG_clsOperands_asString() {
	return this.data.op1 + "#" + this.data.op2;
}

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

//######### QG_clsGenerateTests Class to generate tests ##########
function QG_clsGenerateTests(in_Operator, in_NoNegativeRes, 
								in_MIN, in_MAX, 
								in_NumOfTests) {
	// in_Operator is one of the following: + - * /
	// in_NoNegativeRes defines if the result of subtraction can be negative
	// Operands should be between in_MIN and in_MAX
	
	this.operator = in_Operator;
	this.NoNegativeRes = in_NoNegativeRes;
	this.MIN = parseInt(in_MIN);
	this.MAX = parseInt(in_MAX);
	this.NumOfTests = parseInt(in_NumOfTests);
	
	this.tests = new Array();
	this.testsIndex = 0;
	
	// public methods
	this.generate = QG_clsGenerateTests_generate;

	// "private" methods
	this._operandsExist = QG_clsGenerateTests_operandsExist;
	
	// "private" properties
	this._strValues = new String();
}

function QG_clsGenerateTests_generate() {
	var MAX_NumOfTest = OperandRange(this.MIN, this.MAX) * 
				OperandRange(this.MIN, this.MAX);
	var ops = new QG_clsOperands(this.operator, this.NoNegativeRes, this.MIN, this.MAX);
	var threshold_1, threshold_2;
	var MAX_VAL = 10000;
	
	this._strValues = "";
	threshold_2 = 0;
	
	for (var i = 1; i <= this.NumOfTests; i++) {
		threshold_1 = 0;
		
		while (true) {
			ops.generate();
			
			if (! this._operandsExist(ops.asString()) ) {

				this.tests[this.testsIndex] = 
					new QG_clsData(ops.data.op1, ops.data.op2, ops.data.result);
					
				this.testsIndex++;
				
				this._strValues += ops.asString() + ",";
				
				break;
			}
			
			threshold_1++; if ( threshold_1 == MAX_VAL ) break;
		}

		if ( this.testsIndex >= MAX_NumOfTest) break;
		
		threshold_2++; if ( threshold_2 == MAX_NumOfTest ) break;
	}
	
	delete ops;
}

function QG_clsGenerateTests_operandsExist(in_ops) {
	return this._strValues.indexOf(in_ops) != -1;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

//####### Functions ##########
function OperandRange(in_MIN, in_MAX) {
	return in_MAX - in_MIN + 1;
}

function getRandom() {
	return Math.random();
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

//************* END OF BUSINESS LOGIC ***********************//


//********** PRESENTATION ****************************//
//Validates the parameters
function ValidateParams(in_MIN, in_MAX, in_NumOfTests) {
	
	if ( ! isNumber(in_MIN) ) 
		{ alert("First number in Step 3 is invalid: [" + in_MIN + "]"); return false; }
		
	if ( ! isNumber(in_MAX) ) 
		{ alert("Second number in Step 3 is invalid: [" + in_MAX + "]"); return false; }
		
	if ( parseInt(in_MIN) > parseInt(in_MAX) ) 
		{ alert("Second number must be greater than or equal to First number in Step 3"); return false; }
	
	if ( ! isNumber(in_NumOfTests) ) 
		{ alert("The number of tests to generate in Step 4 is invalid: [" + in_NumOfTests + "]"); return false; }

	if ( ! (parseInt(in_NumOfTests) > 0 &&
		parseInt(in_NumOfTests) <= 500) ) 
		{ alert("The number of tests to generate in Step 4 must be between 1 and 500"); return false; }
	
	return true;
}

function isNumber(v) {
	var re = /^[0-9][0-9]*$/;
	return re.test(v);
}

// Generates the quiz
function GenerateQuiz() {
	
	if (! ValidateParams(
		document.getElementById("frmMain").txtNumMIN.value,
		document.getElementById("frmMain").txtNumMAX.value,
		document.getElementById("frmMain").txtNumOfTests.value
		) ) return;
	
	var v = new QG_clsGenerateTests(
		document.getElementById("frmMain").selOperator.options[document.getElementById("frmMain").selOperator.selectedIndex].value, 
		document.getElementById("frmMain").chkResNoNeg.checked, 
		document.getElementById("frmMain").txtNumMIN.value, 
		document.getElementById("frmMain").txtNumMAX.value,
		document.getElementById("frmMain").txtNumOfTests.value);
	
	v.generate();
	
	var prnt_Layout = ( document.getElementById("frmMain").rdbLayout[0].checked) ? 
				document.getElementById("frmMain").rdbLayout[0].value :
				document.getElementById("frmMain").rdbLayout[1].value;
	
	if ( document.getElementById("frmMain").chkGenAnswers.checked ) ShowQuiz(v, prnt_Layout, true, "Answers");

	ShowQuiz(v, prnt_Layout, false, "Tests");

	delete v;
}

function ShowQuiz(v, in_PrintLayout, in_PrintAnswers, w) {
	var const_MAX_TABLE_COLUMNS = 5;
	
	var msgWindow=window.open("",w,"menubar=yes,scrollbars=yes,resizable=yes", true);
	
	msgWindow.document.open("text/html");
	
	msgWindow.document.write('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">');
	msgWindow.document.write("<html><head><title>Quiz Window</title></head><body>");
	
	//msgWindow.document.write('<script language="JavaScript1.2"><!--');
	//msgWindow.document.write('<form><input type="button" value="Print" onClick="window.print()"><\/form>');
	//msgWindow.document.write('//--></script>');

	msgWindow.document.write('<p align="center"><big><b>Quiz</b></big></p>');

	msgWindow.document.write('<table border="1" width="100%" cellpadding="10">');
	//msgWindow.document.write('<table border="1" cellspacing="1" cellpadding="10" width="100%">');
	
	var i;
	for(i = 0; i < v.testsIndex; ) {

		msgWindow.document.write("<tr>");
		for(col = 1; col <= const_MAX_TABLE_COLUMNS && i < v.testsIndex; col++, i++) {
			// Print one cell
			msgWindow.document.write("<td>");
			msgWindow.document.write('<font size="4"><tt>');
			
			msgWindow.document.write((i+1) + ") " + 
				FormatTest(v.tests[i], v.operator, in_PrintLayout, in_PrintAnswers)
			);
				
			msgWindow.document.write('</tt></font>');
			//msgWindow.document.write("<br /><br />");
			msgWindow.document.write("</td>");
			// END OF Print one cell
		}
		
		msgWindow.document.write("</tr>");
	}

	msgWindow.document.write("</table></body></html>");
	
	msgWindow.document.close();
}

function FormatTest(t, operator, in_PrintLayout, in_PrintAnswers) {
	var res;
	var ans = "________________";
	var op1 = new String(t.op1);
	var op2 = new String(t.op2);
	var delim0, delim1, delim2, delim3;
	
	var s = (in_PrintAnswers) ? t.result : right(ans, Math.max(op1.length, op2.length)+2);
	
	if (in_PrintLayout == "line") {
		if ( t.op1 < 0 ) op1 = "(" + op1 + ")";
		if ( t.op2 < 0 ) op2 = "(" + op2 + ")";
		
		delim0 = "";
		delim1 = " ";
		delim2 = " ";
		delim3 = " = ";
	} else
	{
		delim0 = "";
		delim1 = "<br />";
		delim2 = " ";
		delim3 = "<br />";
		
		op1 = right("xxxxxxxxxx" + op1, Math.max(op1.length, op2.length));
		op2 = right("xxxxxxxxxx" + op2, Math.max(op1.length, op2.length));
		
		var re = /x/gi;
		// Two &nbsp; are added to match the operator sign and a space
		op1 = "&nbsp;"+"&nbsp;"+op1.replace(re, "&nbsp;");
		op2 = op2.replace(re, "&nbsp;");
	};

	res = delim0 + 
		op1 + delim1 +
		operator + delim2 +
		op2 + delim3 +
		s;
			
	return res;
}

function right(str, len) {
	return str.substr(Math.max(0, str.length - len));
}

//  End -->

