/*
  Current version 0.05 November 11, 2004
*/

var mathTestWin, mathAnsWin;
var test, testNoAns;
var gProblems = new Array( );
var gParts     = new Array( ); 
var exp = new Date( );

/* row/page, blank  btwn rows, prob/row, columns/prob, oper/prob; */
var RPP,     BBR,     PPR,     CPP,     OPP,     TUL,     TLL,     RAP,     DPR; 
var RPP_coo, BBR_coo, PPR_coo, CPP_coo, OPP_coo, TUL_coo, TLL_coo, RAP_coo, DPR_coo;
var rb1_coo, rb5_coo, rb6_coo, rb7_coo;

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

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

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 (parms.rb7[1].checked) {
    setCookie("RPP", 10, exp);
    setCookie("BBR",  4, exp);
    setCookie("PPR",  2, exp);
    setCookie("CPP", 35, exp);
    setCookie("OPP",  3, exp);
    setCookie("DPR",  3, exp);
    setCookie("TUL", 10, exp);
    setCookie("TLL",  2, exp);
    setCookie("RAP",  1, exp);
    setCookie("rb1",  2, exp);
    setCookie("rb5",  0, exp);
    setCookie("rb6",  4, exp);
    setCookie("rb7",  0, exp);
  }
  else {
    setCookie(parms.RPP.name, parms.RPP.value, exp);
    setCookie(parms.BBR.name, parms.BBR.value, exp);
    setCookie(parms.PPR.name, parms.PPR.value, exp);
    setCookie(parms.CPP.name, parms.CPP.value, exp);
    setCookie(parms.OPP.name, parms.OPP.value, exp);
    setCookie(parms.DPR.name, parms.DPR.value, exp);
    setCookie(parms.TUL.name, parms.TUL.value, exp);
    setCookie(parms.TLL.name, parms.TLL.value, exp);
    for (i = 0; i < 16; i++) {
      if (parms.rb1[i].checked) 
        setCookie("rb1", i, exp);
    }
    for (i = 0; i < 2; i++) {
      if (parms.rb5[i].checked) 
        setCookie("rb5", i, exp);
    }
    for (i = 0; i < 7; i++) {
      if (parms.rb6[i].checked) 
        setCookie("rb6", i, exp);
    }
    for (i = 0; i < 2; i++) {
      if (parms.rb7[i].checked) 
        setCookie("rb7", i, exp);
    }
    for (i = 0; i < 2; i++) {
      if (parms.RAP[i].checked) 
        setCookie("RAP", i, exp);
    }
  }
}

function getValues( ) {
  var x, i;

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

  BBR_coo = getCookie("BBR");
  if (BBR_coo != null) 
    parms.BBR.value = parseInt(BBR_coo);
  else 
    parms.BBR.value = 4; 

  PPR_coo = getCookie("PPR");
  if (PPR_coo != null) 
    parms.PPR.value = parseInt(PPR_coo);
  else 
    parms.PPR.value = 2; 

  CPP_coo = getCookie("CPP");
  if (CPP_coo != null) 
    parms.CPP.value = parseInt(CPP_coo);
  else 
    parms.CPP.value = 35; 

  OPP_coo = getCookie("OPP");
  if (OPP_coo != null) 
    parms.OPP.value = parseInt(OPP_coo);
  else 
    parms.OPP.value = 3; 

  DPR_coo = getCookie("DPR");
  if (DPR_coo != null) 
    parms.DPR.value = parseInt(DPR_coo);
  else 
    parms.DPR.value = 3; 

  TUL_coo = getCookie("TUL");
  if (TUL_coo != null) 
    parms.TUL.value = parseInt(TUL_coo);
  else 
    parms.TUL.value = 10; 

  TLL_coo = getCookie("TLL");
  if (TLL_coo != null) 
    parms.TLL.value = parseInt(TLL_coo);
  else 
    parms.TLL.value = 2; 

  rb1_coo = getCookie("rb1");
  if (rb1_coo != null) {
    x = parseInt(rb1_coo);
    for (i = 0; i < 16; i++) {
      if (i == x)
        parms.rb1[i].checked = true;
    }
  }
  else
    parms.rb1[2].checked = true;

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

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

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

  RAP_coo = getCookie("RAP");
  if (RAP_coo != null) {
    x = parseInt(RAP_coo);
    for (i = 0; i < 2; i++) {
      if (i == x)
        parms.RAP[i].checked = true;
    }
  }
  else
    parms.RAP[1].checked = true;
}

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 stripAnswers( ) {
  var i;
  var skip;
  var curChar;
  
  testNoAns = "";
  skip = 0;
  
  for (i = 0; i < test.length; i++) {
    curChar = test.charAt(i);

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

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

    if (skip == 0) 
      testNoAns = testNoAns + curChar;
  }
  cleanHorizOrig( );
}

function locateDeepest(prob) {
  var cnt, loc;
  var maxCnt;
  var i;

  maxCnt = 0;
  cnt = 0;
  loc = -1;
  for (i=0; i<prob.length; i++) {
    if (prob.charAt(i) == '(') {
      cnt++;
    }
    if (cnt > maxCnt) {
      maxCnt = cnt;
      loc = i;
    }
    if (prob.charAt(i) == ')') {
      cnt--;
    }
  }
  return(loc);
}

function getValEndLoc(startLoc, prob) {
  var ptr;
 
  ptr = startLoc;
  if (prob.charAt(ptr) == '-') { // looking at a neg # 
    ptr++;
  }
  while ( ((prob.charAt(ptr) >= '0') && (prob.charAt(ptr) <= '9')) || (prob.charAt(ptr) == '.') ) {
    ptr++;
  }
  return(ptr);
}

function getValFrom(start, end, prob) {
  var val;
  var extract;

  val = prob.substring(start, end);
  extract = (1 * val);
  return (extract);
}

function appendAnswer(ptr) {
  var prob, newProb;
  var i, done;
  var deepestParen;
  var ch, operationChar;
  var firstVal, secondVal, firstValStartLoc, firstValEndLoc, secondValStartLoc, secondValEndLoc;
  var operCnt, newVal;
  var deciLoc, endLoc;

  prob = gProblems[ptr];
  newProb = "";
  done = 0;
  while (done == 0) {
    deepestParen = locateDeepest(prob);
    firstValStartLoc = deepestParen+1;
    firstValEndLoc = getValEndLoc(firstValStartLoc, prob);
    operationChar = prob.charAt(firstValEndLoc);
    secondValStartLoc = firstValEndLoc+1;
    secondValEndLoc = getValEndLoc(secondValStartLoc, prob);
    firstVal = getValFrom(firstValStartLoc, firstValEndLoc, prob);
    secondVal = getValFrom(secondValStartLoc, secondValEndLoc, prob);
    switch (operationChar) {
      case '+' : newVal = firstVal + secondVal; break;
      case '-' : newVal = firstVal - secondVal; break;
      case '*' : newVal = firstVal * secondVal; break;
      case '/' : newVal = firstVal / secondVal; break; 
      default  : newVal = 99999; break;
    }
    newProb = prob.substring(0, firstValStartLoc-1) + newVal; 
    newProb = newProb + prob.substring(secondValEndLoc+1, prob.length);
    operCnt = 0;
    for (i=1; i<newProb.length; i++) { // start at posi 1 to avoid leading '-'
      ch = newProb.charAt(i);
      if (ch == '+' || ch == '-' || ch == '*' || ch == '/') 
        operCnt++;
    }
    prob = newProb;
    if (operCnt == 0) {
      done = 1;
    }
  }
  // Trim excessive decimal places
  if (parms.DPR.value < 0) {
    parms.DPR.value *= -1;
  }
  deciLoc = -1;
  for (i = 0; i < prob.length; i++) {
    if (prob.charAt(i) == '.')
      deciLoc = i;
  }
  if (deciLoc == 0) {
    prob = "0" + prob;
    deciLoc++;
  } 
  if (deciLoc > -1) {
    endLoc = deciLoc;
    for (i = 0; i < parms.DPR.value; i++)
      ++endLoc;
    ++endLoc;
    newProb = prob.substring(0, endLoc);
  }

  gProblems[ptr] = gProblems[ptr] + "|" + newProb; // '|' == '=' (replaced later)
}

function buildTemplate( ) {
  var template;
  var tgt, operationCnt;
  var i;
  var located;

  template = "xox";
  for (operationCnt = 1; operationCnt < parms.OPP.value; operationCnt++) {
    tgt = Math.floor(Math.random( ) * (operationCnt + 1)) + 1;
    located = 0;
    for (i = 0; i < template.length; i++) {
      if (template.charAt(i) == 'x') {
        located++;
        if (located == tgt) {
          template = template.substring(0, i) + "(xox)" + template.substring(i+1, template.length);
        }
      }
    }
  }
  return (template);
}

function buildProblem(template) {
  var prob;
  var range;
  var nxtChar;
  var val, op;
  var i;

  prob = "";
  range = (parms.TUL.value - parms.TLL.value) + 1;

  for (i=0; i < template.length; i++) {
    nxtChar = template.charAt(i);
    switch (nxtChar) {
      case '(' :
      case ')' : prob = prob + nxtChar; 
                 break;
      case 'x' : prob = prob + (Math.floor(Math.random( ) * range) + (1 * parms.TLL.value));
                 break;
      case 'o' : op = -1;
                 if (parms.rb1[0].checked) {
                   op = 0;
                 }
                 if (parms.rb1[1].checked) {
                   op = 1;
                 }
                 if (parms.rb1[2].checked) {
                   op = Math.floor(Math.random( ) * 2);
                 }
                 if (parms.rb1[3].checked) {
                   op = 3;
                 }
                 if (parms.rb1[4].checked) {
                   op = 4;
                 }
                 if (parms.rb1[5].checked) {
                   op = Math.floor(Math.random( ) * 2);
                   op += 3;
                 }
                 if (parms.rb1[6].checked) {
                   op = Math.floor(Math.random( ) * 4);
                   if (op > 1)
                     op += 1;
                 }
                 switch (op) {
                   case 0  : prob = prob + "+"; break;
                   case 1  : prob = prob + "-"; break;
                   case 3  : prob = prob + "*"; break;
                   case 4  : prob = prob + "/"; break; // div
                   default : prob = prob + "{op!}"; break;
                 }
                 break;
      default : 
            replacement = replacement + "[E!]"; break;
    }
  }
  return (prob);
}

function padRight(rawProb) {
  var rtnProb;

  rtnProb = rawProb;
  for (i = rtnProb.length; i < parms.CPP.value; i++) {
    rtnProb = rtnProb + "&nbsp;";
  }
  return (rtnProb);
}

function formatTest( ) {
  var r, c, blank;
  var paddedProb;

  for (r = 0; r < parms.RPP.value; r++) {
    if (r < 9) {
      test = test + "&nbsp;";
    }
    if (r < 99) {
      test = test + "&nbsp;";
    }
    test = test + "[" + (r+1) + "]&nbsp;&nbsp;" 
    
    for (c = 0; c < parms.PPR.value; c++) {
      paddedProb = padRight(gProblems[(r * parms.PPR.value) + c]);
      test = test + paddedProb;
    }
    for (blank = 0; blank <= parms.BBR.value; blank++) {
      test = test + "<BR>";
    }
  }
}

function show_gParts(tot, text) {
  var i;
  var tmp;
  
  tmp = "";
  for (i = 0; i < tot; i++) {
    tmp += gParts[i];
  }
  alert(text + ": tot=" + tot + ", gP=" + tmp);
}

function buildOrder(UpLimit, LowLimit) {
  var rtnProbAns, ans, fullans;
  var i, j, k, ch, tot, operCode, operChar;
  var cnt, temp; 
  var deciLoc, endLoc;

  temp = "";
  rtnProbAns = "";
  ans = ""; 

  tot = ((2 * parms.OPP.value) + 1);
  for (i = 0; i < tot; i++) {
    if ((i % 2) == 0) { // this part of the prob is a value
      if ((i > 1) && (gParts[i-1] == '^')) {
        gParts[i] = Math.floor((Math.random( ) * LowLimit));
      }
      else {       
        gParts[i] = Math.floor((Math.random( ) * (UpLimit-(LowLimit-1))));
        gParts[i] += (LowLimit-1);
      }
      if ((i > 1) && (gParts[i] == 0) && (gParts[i-1] == '/')) {
        gParts[i] = 1; // reduce likelihood of div by 0 conditions
      }
    }
    else {   // need an operator
      cnt = 0;
      while (cnt < 7) {
        opercode = Math.floor((Math.random( ) * 5));
        if (operCode == 4) { // minimize exponents
          cnt++;
        }
        else {
          cnt = 100;
        }
      }
      switch(opercode) {
        case 0 : operChar = '+'; break;
        case 1 : operChar = '-'; break;
        case 2 : operChar = '*'; break;
        case 3 : operChar = '/'; break;
        case 4 : operChar = '^'; break;
        default: operChar = '?'; break;
      }
      //if ((operChar == '^') && (i >= 3) && (gParts[i-2] == operChar)) // avoid adjacent ^ ops 
      //  operChar = '+';

      gParts[i] = operChar;
    }
  }
  temp = "";
  for (i = 0; i < tot; i++)
    temp += gParts[i];

  // pattern of #s joined by operations now exists in the gParts array...
  // 'temp' holds the sequence of numbers and operations, but still need to calc an answer...
  
  // sweep exps (and eventually roots) and collapse left to right...

//alert(temp);
//show_gParts(tot, "before exp");

  for (i = 0; i < tot; i++) {
    if (gParts[i] == '^') {
      if (gParts[i-1] == 0 && gParts[i+1] == 0)
        gParts[i-1] = 1; // avoid undef of 0^0
      if (gParts[i+1] == 0)
        ans = 1;
      else {
        ans = gParts[i-1];
        j = gParts[i+1];
        for (k = 1; k < j; k++) {
          ans *= gParts[i-1];
        }
      }
      gParts[i-1] = ans;
      for (j = i + 2; j < tot; j++)
        gParts[j-2] = gParts[j];
      tot -= 2;
      i--; // new operation moved to current loc; must recheck...
    }
  }

//show_gParts(tot, "after exp");
 
  // sweep mult and div and collapse left to right...
  for (i = 0; i < tot; i++) {
    if (gParts[i] == '*' || gParts[i] == '/') {
      if (gParts[i] == '*') 
        ans = gParts[i-1] * gParts[i+1];
      else {
        if (gParts [i+1] == 0) { // fill array with error mssgs to prevent further processing
          ans = 'div by zero';
          for (j = 0; j < tot; j++)
            gParts[j] = 'div by zero';
        }
        else
          ans = gParts[i-1] / gParts[i+1];
      } 
      gParts[i-1] = ans;
      for (j = i + 2; j < tot; j++)
        gParts[j-2] = gParts[j];
      tot -= 2;
      i--; // new operation moved to current loc; must recheck...
    }
  }

//show_gParts(tot, "after md");

  // sweep add and subt and collapse left to right...
  for (i = 0; i < tot; i++) {
    if (gParts[i] == '+' || gParts[i] == '-') {
      if (gParts[i] == '+') 
        ans = gParts[i-1] + gParts[i+1];
      else 
        ans = gParts[i-1] - gParts[i+1];
      gParts[i-1] = ans;
      for (j = i + 2; j < tot; j++)
        gParts[j-2] = gParts[j];
      tot -= 2;
      i--; // new operation moved to current loc; must recheck...
    }
  }

//show_gParts(tot, "after as");

  // Trim excessive decimal places -- duplicate code from 'append answer'
  // (at some point make this a general purpose subroutine
 
  ans = gParts[0] + " ";
  fullans = ans + " ";

  if (parms.DPR.value < 0) {
    parms.DPR.value *= -1;
  }
  deciLoc = -1;

  for (i = 0; i < ans.length; i++) {
    if (ans.charAt(i) == '.')
      deciLoc = i;
  }
  if (deciLoc == 0) {
    ans = "0" + ans;
    deciLoc++;
  } 
  if (deciLoc > -1) {
    endLoc = deciLoc;
    for (i = 0; i < parms.DPR.value; i++)
      ++endLoc;
    ++endLoc;
    ans = ans.substring(0, endLoc);
  }

  // restore full answer if 'e' found (i.e., really large number)
  for (i = 0; i < fullans.length; i++) {
    ch = fullans.charAt(i);
    if ((ch < 0 || ch > 9) && (ch != '.') && (ch != ' ') && (ch != '-')) {
      ans = fullans;
      i = fullans.length;
      // alert("[" + ch + "]");
    }
  }
    
  // done...
  rtnProbAns = temp + "|" + ans;
  return (rtnProbAns);
}

function buildTestWithAnswers( ) {
  var r, c;
  var template;

  for (r = 0; r < parms.RPP.value; r++) {
    for (c = 0; c < parms.PPR.value; c++) {
      template = buildTemplate( );
      gProblems[(r * parms.PPR.value) + c] = buildProblem(template);
      appendAnswer((r * parms.PPR.value) + c);
    }
  }
  formatTest( );
}

function buildExpRootTestWithAnswers( ) {
  var r, c;
  var b, x, ans, localUpperLimit, localLowerLimit;
  var i;
  var prob;

  localUpperLimit = 1 * parms.TUL.value;
  ++localUpperLimit;

  for (r = 0; r < parms.RPP.value; r++) {
    for (c = 0; c < parms.PPR.value; c++) {
      if (parms.rb1[7].checked) { // square test
        b = Math.floor((Math.random( ) * localUpperLimit));
        x = 2; 
        ans = b * b;
        prob =  "" + b + "^" + x + "|" + ans;
        gProblems[(r * parms.PPR.value) + c] = prob;
      }
      if (parms.rb1[8].checked) { // cube test
        b = Math.floor((Math.random( ) * localUpperLimit));
        x = 3; 
        ans = b * b * b;
        prob =  "" + b + "^" + x + "|" + ans;
        gProblems[(r * parms.PPR.value) + c] = prob;
      }
      if (parms.rb1[9].checked) { // N-Exponent test - N as constant
        b = Math.floor((Math.random( ) * localUpperLimit));
        x = parms.TLL.value; 
        ans = b;
        for (i = 1; i < x; i++) {
          ans *= b;
        }
        prob =  "" + b + "^" + x + "|" + ans;
        gProblems[(r * parms.PPR.value) + c] = prob;
      }
      if (parms.rb1[10].checked) { // N-Exponent test - N as upper limit
        b = Math.floor((Math.random( ) * localUpperLimit));
        localLowerLimit = parms.TLL.value;
        ++localLowerLimit;
        x = Math.floor((Math.random( ) * localLowerLimit));
        if ((x + b) == 0)
          ans = "undefined";
        else {
          if (x == 0)
            ans = 1;
          else
            ans = b;
        }
        for (i = 1; i < x; i++) {
          ans *= b;
        }
        prob =  "" + b + "^" + x + "|" + ans;
        gProblems[(r * parms.PPR.value) + c] = prob;
      }
      if (parms.rb1[11].checked) {  // sqrt test
        b = Math.floor((Math.random( ) * localUpperLimit));
        x = b * b;
        prob =  "SqRoot(" + x + ")|" + b;
        gProblems[(r * parms.PPR.value) + c] = prob;
      }
      if (parms.rb1[12].checked) {  // cuberoot test
        b = Math.floor((Math.random( ) * localUpperLimit));
        x = b * b * b;
        prob =  "CubeRoot(" + x + ")|" + b;
        gProblems[(r * parms.PPR.value) + c] = prob;
      }
      if (parms.rb1[13].checked) {  // N-th root test
        b = Math.floor((Math.random( ) * localUpperLimit));
        x = parms.TLL.value;
        ans = b;
        for (i = 1; i < x; i++) {
          ans *= b;
        }
        prob = x + "-Root(" + ans + ")|" + b;
        gProblems[(r * parms.PPR.value) + c] = prob;
      }
      if (parms.rb1[14].checked) {  // N-th root test, N as limit
        b = Math.floor((Math.random( ) * localUpperLimit));
        localLowerLimit = parms.TLL.value;
        x = Math.floor((Math.random( ) * localLowerLimit));
        x++;
        ans = b;
        for (i = 1; i < x; i++) {
          ans *= b;
        }
        prob = x + "-Root(" + ans + ")|" + b;
        gProblems[(r * parms.PPR.value) + c] = prob;
      }
      if (parms.rb1[15].checked) {  // Order of operations (mixed) test
        localLowerLimit = parms.TLL.value;
        ++localLowerLimit;
        prob = buildOrder(localUpperLimit, localLowerLimit);
        gProblems[(r * parms.PPR.value) + c] = prob;
      }
    }
  }
  formatTest( );
}

function generate( ) {
  var i;

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

  test = "<html><head><title>http://www.rbechtold.com/math4</title></head>";
  test = test + "<body><center>";
  test = test + "<h3>";
  test = test + "Test: Advanced-";
  test = test + parms.OPP.value;
  test = test + " (Level ";
  test = test + parms.TUL.value;
  test = test + ")</h3><BR>";

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

  if ((parms.rb1[0].checked) || (parms.rb1[1].checked) || (parms.rb1[2].checked) || 
      (parms.rb1[3].checked) || (parms.rb1[4].checked) || (parms.rb1[5].checked) ||
      (parms.rb1[6].checked)) {
    buildTestWithAnswers( );
  }
  else { 
    buildExpRootTestWithAnswers( );
  }

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

  testNoAns = test;

  stripAnswers( );

  if (parms.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( );

}










 