
/* 
  Current Version: 0.46; November 18, 2006

  Pending Features:
    * Major consideration: Algebraic problems, say, using 25 or so 'patterns' that
      create random, and funny (but somewhat realistic) combinations.  Such as, "At 9:00 am a camel starts walking
      East, at 3 mph, from the west end of a path.  At 1:00 pm a horse starts racing West, at 10 mph, 
      from the other end of the same path.  The path is 50 miles long.  To the nearest minute, 
      when will the camel and horse pass each other?"
      The above is just one example of one pattern. -- NOTE: THIS FEATURE SHOULD HAVE AN OPTION FOR SELF-ADJUSTING
      COMPLEXITY--WHICH REQUIRES COMPUTER-BASED (VS PAPER-BASED) TESTING...

  New Features:
   11/18/2006 Bug Fix -- the option for 'x' instead of '*' wasn't working on 
        random reorder.  Chg'd cb1 to cb1.checked (not sure why it worked at all before).
        Note, if another element is added to the cb1 series they'll all likely need subscripts.
   11/14/2006
	- Per request, added new 'style' where 2nd value = Lower Limit and range is 1
          to upper limit
   06/02/2006
	- Adding 'x' as optional display character for multiplication (per request)
   11/11/2004 (or prior)
    ( ) Add new style -- One value <= 10; Other value between Level# and Limit
    * (To consider: what about powers and roots?  Likely only makes sense up to about level 5)
   11/15/2003
    (o) Make lower limit an entry box (default to 1)
    (o) Move location of lower limit box up next to Math test level #
    (o) Change 1st style option to Both values within Level# and Lower Limit
    (o) Add adjustable column width, default 8 (near bottom)
    ( ) Add new style -- All values, including answers <= Level#
    6/3/03
      * Adding ability to set problems per row (count) and rows per page.
      * Add three new test types
      - Mixed Addition and Subtraction
      - Mixed Multiplication and Division
      - Mixed everything
  Known Bugs: 
    6/1/03 (ver 0.14b June 01)
      (a) Subt test creates strange results when using 
          level 57, style 3, and lower limit always = Lvl#
          FIXED 6/3/03.
*/

var mathTestWin;
  var test; 
  var testNoAns;
  var testTop = new Array( );
  var testMid = new Array( );
  var testBot = new Array( );
  var testSym = new Array( );
  var totPPR = 10;
  var totRPP = 10;
  var gTot = 100;
  var alertTest = 0;

  var rb1_coo, rb2_coo, rb4_coo, rb5_coo, rb6_coo, rb7_coo, TLN_coo, PPR_coo, RPP_coo, CMT_coo, NBR_coo;
  var TLL_coo, CPP_coo, rb8_coo;
  var exp = new Date( );

function setCookie(name, value, expires) {
  document.cookie = escape(name) + "=" + escape(value) + "; path=/" +
    ((expires == null) ? "" : "; expires=" + expires.toGMTString( ));
}

function getCookie(name) {
  var cName = name + "=";
  var dc = document.cookie;
  var begin, end;

  if (dc.length > 0) {
    begin = dc.indexOf(cName);
    if (begin != -1) {
      begin += cName.length;
      end = dc.indexOf(";", begin);
      if (end == -1) {
        end = dc.length;
      }
      return unescape(dc.substring(begin, end));
    }
  }
  return null;
}

function setValues( ) {
  var i;

  if (mathParms.rb7[1].checked) {
    setCookie("TLN", 10, exp);
    setCookie("TLL", 1, exp);
    setCookie("PPR", 10, exp);
    setCookie("RPP", 10, exp);
    setCookie("CMT", 0, exp);
    setCookie("NBR", 2, exp);
    setCookie("CPP", 8, exp);
    setCookie("rb1", 0, exp);
    setCookie("rb2", 0, exp);

    setCookie("rb4", 0, exp);
    setCookie("rb5", 0, exp);
    setCookie("rb6", 5, exp);
    setCookie("rb7", 0, exp);
    setCookie("rb8", 0, exp);
  }
  else {
    setCookie(mathParms.TLN.name, mathParms.TLN.value, exp);
    setCookie(mathParms.TLL.name, mathParms.TLL.value, exp);
    setCookie(mathParms.PPR.name, mathParms.PPR.value, exp);
    setCookie(mathParms.RPP.name, mathParms.RPP.value, exp);
    setCookie(mathParms.CMT.name, mathParms.CMT.value, exp);
    setCookie(mathParms.NBR.name, mathParms.NBR.value, exp);
    setCookie(mathParms.CPP.name, mathParms.CPP.value, exp);

    for (i = 0; i < 7; i++) {
      if (mathParms.rb1[i].checked) 
        setCookie("rb1", i, exp);
    }
    for (i = 0; i < 5; i++) {
      if (mathParms.rb2[i].checked) 
        setCookie("rb2", i, exp);
    }

    for (i = 0; i < 2; i++) {
      if (mathParms.CMT[i].checked) 
        setCookie("CMT", i, exp);
    }
    for (i = 0; i < 2; i++) {
      if (mathParms.rb4[i].checked) 
        setCookie("rb4", i, exp);
    }
    for (i = 0; i < 2; i++) {
      if (mathParms.rb5[i].checked) 
        setCookie("rb5", i, exp);
    }
    for (i = 0; i < 7; i++) {
      if (mathParms.rb6[i].checked) 
        setCookie("rb6", i, exp);
    }
    for (i = 0; i < 2; i++) {
      if (mathParms.rb7[i].checked) 
        setCookie("rb7", i, exp);
    }
    for (i = 0; i < 2; i++) {
      if (mathParms.rb8[i].checked) 
        setCookie("rb8", i, exp);
    }
  }
}

function getValues( ) {

  rb1_coo = getCookie("rb1");
  if (rb1_coo != null) {
    x = parseInt(rb1_coo);
    for (i = 0; i < 7; i++) {
      if (i == x)
        mathParms.rb1[i].checked = true;
    }
  }
  else
    mathParms.rb1[0].checked = true;
    
  TLN_coo = getCookie("TLN");
  if (TLN_coo != null) 
    mathParms.TLN.value = parseInt(TLN_coo);
  else 
    mathParms.TLN.value = 10; 
  
  TLL_coo = getCookie("TLL");
  if (TLL_coo != null) 
    mathParms.TLL.value = parseInt(TLL_coo);
  else 
    mathParms.TLL.value = 1; 
  
  rb2_coo = getCookie("rb2");
  if (rb2_coo != null) {
    x = parseInt(rb2_coo);
    for (i = 0; i < 5; i++) {
      if (i == x)
        mathParms.rb2[i].checked = true;
    }
  }
  else
    mathParms.rb2[0].checked = true;

  PPR_coo = getCookie("PPR");
  if (PPR_coo != null) 
    mathParms.PPR.value = parseInt(PPR_coo);
  else 
    mathParms.PPR.value = 10;

  RPP_coo = getCookie("RPP");
  if (RPP_coo != null) 
    mathParms.RPP.value = parseInt(RPP_coo);
  else 
    mathParms.RPP.value = 10;

  rb4_coo = getCookie("rb4");
  if (rb4_coo != null) {
    x = parseInt(rb4_coo);
    for (i = 0; i < 2; i++) {
      if (i == x)
        mathParms.rb4[i].checked = true;
    }
  }
  else
    mathParms.rb4[0].checked = true;

  rb5_coo = getCookie("rb5");
  if (rb5_coo != null) {
    x = parseInt(rb5_coo);
    for (i = 0; i < 2; i++) {
      if (i == x)
        mathParms.rb5[i].checked = true;
    }
  }
  else
    mathParms.rb5[0].checked = true;

  CMT_coo = getCookie("CMT");
  if (CMT_coo != null) {
    x = parseInt(CMT_coo);
    for (i = 0; i < 2; i++) {
      if (i == x)
        mathParms.CMT[i].checked = true;
    }
  }
  else
    mathParms.CMT[0].checked = true; 

  rb6_coo = getCookie("rb6");
  if (rb6_coo != null) {
    x = parseInt(rb6_coo);
    for (i = 0; i < 7; i++) {
      if (i == x)
        mathParms.rb6[i].checked = true;
    }
  }
  else
    mathParms.rb6[5].checked = true;

  NBR_coo = getCookie("NBR");
  if (NBR_coo != null) 
    mathParms.NBR.value = parseInt(NBR_coo);
  else 
    mathParms.NBR.value = 2; 

  CPP_coo = getCookie("CPP");
  if (CPP_coo != null) 
    mathParms.CPP.value = parseInt(CPP_coo);
  else 
    mathParms.CPP.value = 8;

  rb7_coo = getCookie("rb7");
  if (rb7_coo != null) {
    x = parseInt(rb7_coo);
    for (i = 0; i < 2; i++) {
      if (i == x)
        mathParms.rb7[i].checked = true;
    }
  }
  else
    mathParms.rb7[0].checked = true;

  rb8_coo = getCookie("rb8");
  if (rb8_coo != null) {
    x = parseInt(rb8_coo);
    for (i = 0; i < 2; i++) {
      if (i == x)
        mathParms.rb8[i].checked = true;
    }
  }
  else
    mathParms.rb8[0].checked = true;
}

function padOutputRight(data) {
  var textOut;
  var len;
  var i;


  len = data.length;
  test = test + data;

  for (i = 0; i < (mathParms.CPP.value - len); i++) {
    test = test + "&nbsp;";
  }

}

function padOutput(data) {
  var textOut;
  var len;
  var i;

  textOut = " " + data;

  len = textOut.length;

  for (i = 0; i < (mathParms.CPP.value - len); i++) {
    test = test + "&nbsp;";
  }
  test = test + data;
}

function reorder(pass) {
  var tmp;

  for (var i = 0; i < pass; i++) {
    for (var n = 0; n < gTot; n++) {
      loc = Math.floor(Math.random( ) * gTot);
      tmp = testTop[i]; testTop[i] = testTop[loc]; testTop[loc] = tmp;
      tmp = testMid[i]; testMid[i] = testMid[loc]; testMid[loc] = tmp;
      tmp = testBot[i]; testBot[i] = testBot[loc]; testBot[loc] = tmp;
      tmp = testSym[i]; testSym[i] = testSym[loc]; testSym[loc] = tmp;
    }
  }
}

function reduceAdjacents(max) {
  var flip;

  flip = 1;
  while ((flip == 1) && (max-- > 0)) {
    flip = 0;
    for (var i = 1; i < gTot; i++) {
      if (           ((testTop[i]==testTop[i-1 ]) && (testMid[i]==testMid[i-1 ]))   ||
           (i > 1 && ((testTop[i]==testTop[i-2 ]) && (testMid[i]==testMid[i-2 ])) ) ||
           (i > totPPR && ((testTop[i]==testTop[i-totPPR]) && (testMid[i]==testMid[i-totPPR])) ) 
         ) {
        flip = 1;
        loc = Math.floor(Math.random( ) * gTot);
        tmp = testTop[i]; testTop[i] = testTop[loc]; testTop[loc] = tmp;
        tmp = testMid[i]; testMid[i] = testMid[loc]; testMid[loc] = tmp;
        tmp = testBot[i]; testBot[i] = testBot[loc]; testBot[loc] = tmp;
        tmp = testSym[i]; testSym[i] = testSym[loc]; testSym[loc] = tmp;
      }
    }
  }
  // alert("Reduction Passes = " + (100-max));
}

function buildHorizontalOutput(symbol) {
  // test = test + "<br>";

  localSymbol = symbol;
  for (r = 0; r < totRPP; r++) {

    if (r >= 9)
      lineLabel = "(" + (r+1) + ")";
    else
      lineLabel = "&nbsp;(" + (r+1) + ")";
    test = test + lineLabel + "&nbsp;&nbsp;&nbsp;&nbsp;";

    for (c = 0; c < totPPR; c++) {
      if (symbol == "X")
        localSymbol = testSym[(r * totPPR) + c];
      thisProb = testTop[(r * totPPR) + c] + localSymbol + testMid[(r * totPPR) + c];

      if (document.mathParms.rb8[1].checked)
        thisProb = thisProb + "|";
      else
        thisProb = thisProb + "=";

      thisProb = thisProb + testBot[(r * totPPR) + c];
      padOutputRight(thisProb);
    }
    test = test + "<br>";


    if (r < (totRPP-1)) { // 2 is std; 1st is reg eol, 2nd is 1st blank
      blankRows = mathParms.NBR.value;
      while (blankRows > 0) {
        test = test + "<br>";
        blankRows--;
      }
    }
  }  
}

function buildOutput(symbol) {
  var r, t, m, b, x;
  var blankRows;
  var lineLabel;
  var localSymbol;

  // test = test + "<br>";

  if (document.mathParms.rb4[0].checked) {
    reorder(17);
    reduceAdjacents(100);
  }

  if (document.mathParms.rb8[1].checked) {
    buildHorizontalOutput(symbol);
  }
  else {
    localSymbol = symbol;
    for (r = 0; r < totRPP; r++) {
      if (r >= 9)
        lineLabel = "(" + (r+1) + ")";
      else
        lineLabel = "&nbsp;(" + (r+1) + ")";
      test = test + lineLabel;

      for (t = 0; t < totPPR; t++) {
        padOutput(testTop[(r * totPPR) + t]);
      }
      test = test + "<br>";

      lineLabel = "&nbsp;&nbsp;&nbsp;&nbsp;";

      test = test + lineLabel;

      for (m = 0; m < totPPR; m++) {
        if (symbol == "X")
          localSymbol = testSym[(r * totPPR) + m];
        padOutput(localSymbol + testMid[(r * totPPR) + m]);
      }
      test = test + "<br>";

      test = test + lineLabel;

      for (m = 0; m < totPPR; m++) {
        padOutput(" ====");
      }
      test = test + "<br>";

      test = test + lineLabel;
 
      for (b = 0; b < totPPR; b++) {
        padOutput(testBot[(r * totPPR) + b]);
      }

      if (r < (totRPP-1)) { // 2 is std; 1st is reg eol, 2nd is 1st blank
        blankRows = mathParms.NBR.value;
        while (blankRows > 0) {
          test = test + "<br>";
          blankRows--;
        }
      }
    }
  }
}

function stripAnswers( ) {
  var i;
  var skip;
  var curChar;
  var stateLvl;
  var openBrace;
  var closeBrace;

  openBrace = 0; 
  closeBrace = 0;
  stateLvl = 0;
  skip = 0;
  testNoAns = "";

  for (i = 0; i < test.length; i++) {
    curChar = test.charAt(i);
    if (closeBrace == 0)
      testNoAns = testNoAns + curChar;
    if (curChar == ')' && closeBrace == 0) {
      closeBrace = 1;
      curChar = '';
    }
    if (closeBrace == 1) {
      if ((stateLvl == 0) && (curChar == '='))
        stateLvl = 1;
      if (skip == 0) 
        testNoAns = testNoAns + curChar;
      else {
        if ((curChar == '>') && (openBrace == 0)) {
          stateLvl = 0;
          skip = 0;
        } 
        openBrace = 0;
      }
      if ((stateLvl == 1) && (curChar == '>')) {
        testNoAns = testNoAns + "<br>";
        skip = 1;
        openBrace = 1;
      }
    }
  }
}

function cleanHorizOrig( ) {
  var testClean;

  testClean = "";
  for (i = 0; i < test.length; i++) {
    curChar = test.charAt(i);
    if (curChar == '|')
      curChar = '=';
    testClean = testClean + curChar;
  }
  test = testClean;
}

function stripHorizAnswers( ) {
  var i;
  var skip;
  var curChar;
  var okToStart;

  openBrace = 0; 
  stateLvl = 0;
  skip = 0;
  testNoAns = "";

  for (i = 0; i < test.length; i++) {
    curChar = test.charAt(i);

    if ( (skip == 1) && (curChar >= '0' && curChar <= '9') )
      testNoAns = testNoAns + "&nbsp;"
    else 
      skip = 0;

    if (curChar == '|') {
      testNoAns = testNoAns + '=';
      skip = 1;
    }
    if (skip == 0) 
      testNoAns = testNoAns + curChar;
  }
  cleanHorizOrig( );
}

function insertHardBreaks(mssg) {
  var text;
  var cnt = 0;
  
  text = "";
  for (var i = 0; i < mssg.length; i++) {
    if (mssg.charAt(i) == '$') {
      text = text + "<BR>";
    }
    else
      text = text + mssg.charAt(i);
  }
  text = text + "<BR>";
  return (text);
}

function generate( ) {
  var i;
  var top, mid, bot;
  var testTypeName;
  var testStyleCode;
  var lvl;
  var upperLimit;
  var lowerLimit;
  var testSymbol;
  var randVal;
  var rejectCount;
  var quotientTooLarge;
  var newText;
  var secondVal;

  rejectCount = 0;
  exp.setTime(exp.getTime( ) + (1000 * 60 * 60 * 24 * 30)); // cookies expire in 30 days

  for (var i = 0; i < document.mathParms.rb1.length; i++) {
    if (document.mathParms.rb1[i].checked) break;
  }
  testTypeName = document.mathParms.rb1[i].value;

  for (var i = 0; i < document.mathParms.rb2.length; i++) {
    if (document.mathParms.rb2[i].checked) break;
  }
  testStyleCode = document.mathParms.rb2[i].value;

  lvl = document.mathParms.TLN.value;
  upperLimit = (1 * lvl);
  //if (document.mathParms.rb2[1].checked) {
  //    upperLimit = 10;
  //}

  lowerLimit = (1 * mathParms.TLL.value);

  //New Style '5' -- 2nd value = LL and range is 1 to Level#
  secondVal = lowerLimit;
  if ((document.mathParms.rb2[2].checked) || (document.mathParms.rb2[4].checked))
    secondVal = lowerLimit;
  if (document.mathParms.rb2[4].checked)
    lowerLimit = 1;

  totPPR = mathParms.PPR.value;
  totRPP = mathParms.RPP.value;
  gTot = totPPR * totRPP;

  testNoAns = "";

  test = "<html><head><title>http://www.rbechtold.com/math</title></head>";

  test = test + "<body><center>";

  if (document.mathParms.opTitle.value.length > 0) {
    newText = insertHardBreaks(document.mathParms.opTitle.value);
    test = test + "<h3>" + newText + "</h3>";
  }
  else {
    test = test + "<h3>" + testTypeName + " Test: Level " + document.mathParms.TLN.value + 
         ", " + testStyleCode + "</h3>";
  }

  for (var i = 0; i < 7; i++) {
    if (document.mathParms.rb6[i].checked)
      test = test + "<font face=Courier size=" + (7-i) + ">";
  }

  // if the test is going to be built using random values... and/or mixed operations...
  if ((mathParms.CMT[1].checked) ||
      (testTypeName.charAt(0) == 'B') ||
      (testTypeName.charAt(0) == 'C') ||
      (testTypeName.charAt(0) == 'E') ) {

    for (i = 0; i < gTot; i++) {
      testTop[i] = Math.floor(Math.random( ) * ((upperLimit+1) - lowerLimit)) + lowerLimit;
      testMid[i] = Math.floor(Math.random( ) * ((upperLimit+1) - lowerLimit)) + lowerLimit;

      if (document.mathParms.rb2[1].checked) 
        testMid[i] = Math.floor(Math.random( ) * (11 - lowerLimit)) + lowerLimit;
      if ((document.mathParms.rb2[2].checked) || (document.mathParms.rb2[4].checked))
        testMid[i] = (1 * secondVal);

      // This block is for handling mixed (and currently always random) tests
      // First, figure out randomly what test operation to use
      testSymbol = testTypeName.charAt(0);
      if (testSymbol == 'B') {
        if (Math.floor(Math.random( ) * 1001) < 501)
          testSymbol = 'A';
        else
          testSymbol = 'S';
      }
      if (testSymbol == 'C') {
        if (Math.floor(Math.random( ) * 1001) < 501)
          testSymbol = 'M';
        else
          testSymbol = 'D';
      }
      if (testSymbol == 'E') {
        randVal = Math.floor(Math.random( ) * 1001);
        if (randVal < 250) {
          testSymbol = 'A';
        }
        else {
          if (randVal < 500) {
            testSymbol = 'S';
          }
          else {
            if (randVal < 751) {
              testSymbol = 'M';
            }
            else {
              testSymbol = 'D';
            }
          }
        }
      }
      // If substraction, ensure testMid is less than test top
      if (testSymbol == 'S') {
        while (testTop[i] < testMid[i]) {
          testTop[i] = Math.floor(Math.random( ) * ((upperLimit+1) - lowerLimit)) + lowerLimit;
          testMid[i] = Math.floor(Math.random( ) * ((upperLimit+1) - lowerLimit)) + lowerLimit;
          if (document.mathParms.rb2[1].checked) 
            testMid[i] = Math.floor(Math.random( ) * (11 - lowerLimit)) + lowerLimit;
          if ((testTop[i] < testMid[i]) && ((document.mathParms.rb2[2].checked) || (document.mathParms.rb2[4].checked))) {
            testMid[i] = (1 * secondVal);
          }
        }
      }
      // Ensure division tests are integers, and the quotient < upperLimit
      if (testSymbol == 'D') {
        var ans;
        quotientTooLarge = 1;
        while (quotientTooLarge > 0) {
          testTop[i] = Math.floor(Math.random( ) * ((upperLimit+1) - lowerLimit)) + lowerLimit;
          testMid[i] = Math.floor(Math.random( ) * ((upperLimit+1) - lowerLimit)) + lowerLimit;
          if (testMid[i] == 0) {
            if (Math.floor(Math.random( ) * 1001) < 501)
              testMid[i] = 1;
            else
              testMid[i] = 2;
          }
          ans = testTop[i] * testMid[i];
          if (ans <= upperLimit) // All three values are below upperLimit AND with integer result
            quotientTooLarge = 0;
        } 
      }
      // Now, actually calculate answers
      if (testSymbol == 'A') {
         testBot[i] = testTop[i] + testMid[i];
         testSym[i] = "+"; 
      }
      if (testSymbol == 'S')  {
         testBot[i] = testTop[i] - testMid[i];
         testSym[i] = "-";
      }
      if (testSymbol == 'M') {
         testBot[i] = testTop[i] * testMid[i];
         testSym[i] = "*";
         if (document.mathParms.cb1.checked)
           testSym[i] = "x"; // loc 3
      }
      if (testSymbol == 'D') {
         testBot[i] = testMid[i];
         testMid[i] = testTop[i];
         if (testMid[i] == 0)
           testMid[i]++;
         testTop[i] = testBot[i] * testMid[i];
         testSym[i] = "/";
      }
      if (document.mathParms.rb2[3].checked && (testBot[i] > upperLimit)) { // limit answer to <= Level#
        i--;
        rejectCount++;
        if (rejectCount > 1000)  // too many rejections, just continue...
          i++;
      }
    }
    switch (testTypeName.charAt(0)) {
      case 'A' : buildOutput("+"); break;
      case 'S' : buildOutput("-"); break;
      case 'M' : if (document.mathParms.cb1.checked)
                   buildOutput("x"); // loc 4
                 else
                   buildOutput("*");
                 break;
      case 'D' : buildOutput("/"); break;
      default  : buildOutput("X"); break; // Mixed operation test
    }
  }
  else {
    // ADDITION and MULTIPLICATION
    // Generally, create tests of the form 10+10, 10+9, 10+8... 10+1, 9+10, 9+9...
    // So, decrement 'top' via the outer loop from upperLimit to lowerLimit
    // and, decrement 'mid' via the inner loop (again, from upper to lower)
    if (testTypeName.charAt(0) == 'A' || 
        testTypeName.charAt(0) == 'M' ) {
      top = upperLimit;
      mid = (1 * lvl);
      for (i = 0; i < gTot; i++) {
        testTop[i] = top;
        testMid[i] = mid;
        if (document.mathParms.rb2[1].checked) 
          testMid[i] = Math.floor(Math.random( ) * (11 - lowerLimit)) + lowerLimit;
        if ((document.mathParms.rb2[2].checked) || (document.mathParms.rb2[4].checked)) {
          testMid[i] = (1 * secondVal);
        }
        if (testTypeName.charAt(0) == 'A')
          testBot[i] = testTop[i] + testMid[i];
        else 
          testBot[i] = testTop[i] * testMid[i];
        mid--;
        if (mid < lowerLimit) {
          top--;
          mid = (1 * lvl);
          if (top < lowerLimit) {
            top = upperLimit;
            mid = (1 * lvl);
          }
        }
        if (document.mathParms.rb2[3].checked && (testBot[i] > upperLimit)) { // limit answer to <= Level#
          i--;
          rejectCount++;
          if (rejectCount > 1000)  // too many rejections, just continue...
            i++;
        }
      }
      if (testTypeName.charAt(0) == 'A') {
        buildOutput("+");
      }
      else {
        if (document.mathParms.cb1.checked) // loc 5
          buildOutput("x"); 
        else
          buildOutput("*");
      }
    }
    // SUBTRACTION
    if (testTypeName.charAt(0) == 'S') {
      top = upperLimit;
      mid = (1 * lvl);
      if (mid > top)
        mid = top;
      for (i = 0; i < gTot; i++) {
        testTop[i] = top;
        testMid[i] = mid;
        if ((document.mathParms.rb2[2].checked) || (document.mathParms.rb2[4].checked)) {
          testMid[i] = (1 * secondVal);
          if (testMid[i] > testTop[i]) 
            testTop[i] = testMid[i]; // top must always be >= mid;
        }

        if ((document.mathParms.rb2[2].checked) || (document.mathParms.rb2[4].checked))
          mid = secondVal; // force iteration on top# if mid# is a constant
        testBot[i] = testTop[i] - testMid[i];

        mid--;
        if (mid < lowerLimit) {
          top--;
          if (top >= (1 * lvl))
            mid = (1 * lvl);
          else
            mid = top;
        }
        if (top < lowerLimit) {
          top = upperLimit;
          mid = (1 * lvl);
          if (mid > top)
            mid = top;
        }
        if (document.mathParms.rb2[3].checked && (testBot[i] > upperLimit)) { // limit answer to <= Level#
          i--;
          rejectCount++;
          if (rejectCount > 1000)  // too many rejections, just continue...
            i++;
        }
      }
      buildOutput("-");
    }
    // DIVISION
    if (testTypeName.charAt(0) == 'D') {
      if (lowerLimit < 1)
        lowerLimit = 1;
      top = upperLimit;
      mid = (1 * lvl);
      for (i = 0; i < gTot; i++) {
        testMid[i] = top;
        if ((document.mathParms.rb2[2].checked) || (document.mathParms.rb2[4].checked))
          testMid[i] = (1 * secondVal);
        testBot[i] = mid;
        testTop[i] = testBot[i] * testMid[i];
        mid--;
        if (mid < lowerLimit) {
          top--;
          mid = (1 * lvl);
        }
        if (top < lowerLimit) {
          top = upperLimit;
          mid = (1 * lvl);
        }
        if (document.mathParms.rb2[3].checked && (testBot[i] > upperLimit)) { // limit answer to <= Level#
          i--;
          rejectCount++;
          if (rejectCount > 1000)  // too many rejections, just continue...
            i++;
        }
      }
      buildOutput("/");
    }
  }

  test = test + "</center>";
  test = test + "</center>";

  if (document.mathParms.opInst.value.length > 0) {
    for (var r = 0; r < mathParms.NBR.value; r++) {
      test = test + "<BR>";
    }
    newText = insertHardBreaks(document.mathParms.opInst.value);
    test = test + "<B>" + newText + "</B>";
  } 

  test = test + "</font></body></html>";  

  if (document.mathParms.rb8[1].checked) 
    stripHorizAnswers( );
  else 
    stripAnswers( );

  if (document.mathParms.rb5[0].checked) {
    mathAnsWin = window.open("", "maw",
      "width=750,height=400,scrollbars=yes,resizable=yes,status=yes,menubar=yes,toolbar=yes");
    mathAnsWin.document.write(test);
    mathAnsWin.focus( );
    mathAnsWin.document.close( );
  }

  mathTestWin = window.open("", "mtw",
    "width=750,height=400,scrollbars=yes,resizable=yes,status=yes,menubar=yes,toolbar=yes");
  mathTestWin.document.write(testNoAns);
  mathTestWin.focus( );
  mathTestWin.document.close( );

}

function printGen( ) {
  mathTestWin.print( );
}

function printAns( ) {
  mathAnsWin.print( );
}
