// equation functions 


//======================================== 
// process a  list of equations, that may have IF subsets.
// tonode: the node being worked on. Its value attribute may be set, and its attributes may be read..
//         If tonode='',  equations without varname= do not have their values saved
//       jan09: return cumulative  "saved" (from equations with no = signs). These are used by <SET> and other special tags 
//       nov09:  set a global: doEquationsSet, that list names of all variables set in this call to e2_doEquations
// setNode: the node that contains a list of equations. I.e.; a SET node (or a PARAMS node)
// anif : used in recursive calls (do NOT specify on the original call)
// noVset: if 1, then do not save "cumulative" values to the tonode (just return them)

function  E2_doEquations(tonode,setNode,anif,noVset){ 
var dostop,cumvalue,documvalue,addcum;
var ntype,anVal,addto,glue,istrue,arf,i,anode,flist;
var ndid=0;
var doList,ff,loopVar,ff2,loopStart,loopEnd,loopStep,jdo,j ;
var typeok,startTime=0 ;
var  nowTime,foof ;

if (arguments.length<4) startTime=new Date() ;

doEquationsSet='' ;                     // global: list of variables set in this call

if (arguments.length<3) anif=0;

cumvalue=''; documvalue=0; dostop=0 ;
if (arguments.length<4) noVset=0;

for (var i=0; i<setNode.childNodes.length;i++) {
//  E2_writeStatus(anif+':SST: '+i+' :: '+startTime+'-'+nowTime+'='+foof);
  if (startTime!==0) {
        nowTime=new Date() ;
        foof=nowTime-startTime ;
        if (foof>2000) {
              displayBusyAlert(2);
              startTime=0;
        }
  }
  typeok=0;
  anode=setNode.childNodes[i];
  if (anode.nodeType==3) {                      // text node
     ntype="VALUE";
     flist=anode.nodeValue ;
  } else {                                      // not text, either a STOP or an IF 
     ntype=strip(anode.nodeName.toUpperCase()) ;
 }


  if (ntype=='STOP') {
        dostop=1;
        break ;
  }

  if (ntype=='BREAK') {
        dostop=2;
        break ;
  }

  if (ntype=='CONTINUE') {
        dostop=3;
        break ;
  }


  if (ntype=='VALUE') {                                                         // a hack
       typeok=1;
       addto=E2_doEquations1(tonode,flist,0,noVset);                       // evaluate an array of equations, each element may have several equations on several lines
       if (typeof(addto)!='undefined' && addto.length>0) {
            documvalue++ ;
            cumvalue=cumvalue+addto+' ';
        }
        ndid=ndid+flist.length ;
   }                                    // <VALUE> 

  if (ntype=='IF') {
       typeok=1;
       isThen=E2_getNodes(anode,'THEN',1);
       if (isThen.length>0) {                                // use advanced syntax 
          istrue=E2_node_doIf(anode,2) ;                // first text node within  <if> .... </if>
          if (istrue==0) {                              // use ELSE?
             isElse=E2_getNodes(anode,'ELSE',1);
             if (isElse.length==0) continue ;           // no else, so do nothing
             anode=isElse[0]
         } else {
            anode=isThen[0];
          }
       } else {                                         // value attribute inside of <if  ... >
          istrue=E2_node_doIf(anode,1) ;
          if (istrue==0) continue ;
       }

       var glue=anif+1;
       addcum=E2_doEquations(tonode,anode,glue,noVset,startTime) ;         
       if (addcum.gotcum==1) {
           documvalue=1 ;
           cumvalue=cumvalue+' '+addcum.cumvalue;
        }
       if (addcum.done>0) {
          dostop=addcum.done ;break ;
       }
   }                    // IF

  if (ntype=='DO') {
       typeok=1;
       doList=getAttributes(anode,'DO VALUE FOR','');                //  i=istart to iend by istep 
       if (doList=='') {
                alert('ERROR: E2_doEquations: DO without instructions ');
                return null ;
       }
       if (doList.indexOf('=')<0) {
                alert('ERROR: E2_doEquations: DO without i=  : '+doList);
                return null ;
       }
       ff=doList.split('=');
       loopVar=strip(ff[0]);
       ff2=ff[1].split(',');
       if (ff2.length<2) {
                alert('ERROR: E2_doEquations: DO without end: '+doList);
                return null ;
       }
       loopStart=svy_getVal(ff2[0],'',tonode);
       if (isNaN(loopStart)) {
                alert('ERROR: E2_doEquations: do Start not a number: '+doList);
                return null ;
       }

       loopEnd=svy_getVal(ff2[1],'',tonode);
       if (isNaN(loopEnd)) {
                alert('ERROR: E2_doEquations: do End not a number: '+doList);
                return null ;
       }

       loopStep=1;
       if (ff2.length>2)  {
          loopStep=svy_getVal(ff2[2],'',tonode);
          if (isNaN(loopStep) || loopStep<0 ) {
                alert('ERROR: E2_doEquations: do Step not a number: '+doList);
                return null ;
          }
       }
       jdo=loopStart-loopStep ;
       for (var j=0;j<10000000;j++) {
            jdo=parseFloat(jdo)+parseFloat(loopStep);
            if (loopStep>0 && jdo>loopEnd) break ;
            if (loopStep<0 && jdo<loopEnd) break ;
            Save_Global(loopVar,jdo);
            addcum=E2_doEquations(tonode,anode,1,noVset,startTime) ;         
    
        if (addcum.gotcum==1) {
               documvalue=1 ;
               cumvalue=cumvalue+' '+addcum.cumvalue;
            }
            if (addcum.done>0 && addcum.done<3) {
               dostop=addcum.done ;break ;
            }
           jdo=Read_Global(loopVar,'');
           if (jdo==='') break;
       }                                        // repeat the DO 
   }                    // DO


   if (dostop==1) break ;                       // dostop ==2 means "break out of loop, but not out of all equations ",3 means "back to top of loop"

   if (typeok==0 ) {                    // not a special element, treat as a 
        alert('Error: an unsupported element in an equation: '+ntype+' \n'+setNode.innerHTML);
        break ;
   }


}                       // for i child
 
if (anif==0) {
   if (tonode!=='' && documvalue>0 && noVset!=1 ) {
         ok= set_elemValue(tonode,cumvalue);       //set the tonode's value attribute 
  }

  return cumvalue ;                           // end 
} else {                                // end of this recursive call, return to caller 
   arf=new Object() ;
   arf.done=dostop ; arf.gotcum=0; arf.cumvalue='' ;
   arf.ndid=arf.ndid+ndid ;
   if (documvalue) {
        arf.gotcum=1 ;
        arf.cumvalue=cumvalue;
   }
   return arf ;                         // end of this recursive call 
}

}                       //doEqu
  
//========== return doEq
// run the functions / math operations in funclist; and/or save global variables.

function E2_doEquations1(anode,funclist) {
//E2_writeStatus('in doequeations '+funclist);
var tmp,i,fuse,stuff,ares,anName,cumvalue;
var tmp1,tmp2,tmp3,itmp,aline;
var   fuseExp=[];

cumvalue='' ;


if (typeof(funclist)=='string') {               // a small hack
   tmp=funclist ;
   funclist=[] ;
   funclist[0]=tmp;
}

for (var i=0; i<funclist.length;i++) {

    funclist2=funclist[i].split('\n');  
    for (var ii=0; ii<funclist2.length;ii++) {
        fuse=strip(funclist2[ii]) ; 
        if (fuse.substr(fuse.length-1)==';') fuse=fuse.substr(0,fuse.length-1);         // strip off trailing ;
        if (fuse=='') continue ;

// look for vv[m:n]=x syntax
        if (fuse.indexOf('=')>-1 && fuse.indexOf('[')<fuse.indexOf('=') ) {     // an equation,
          tmp1=parseAt(fuse,'=');
          tmp2=parseUsing(tmp1[0],'[',']',1);
          if (tmp2[1].indexOf(':')>0 ) {                // expand these equations
               tmp3=parseAt(tmp2[1],':') ;
               if (isNaN(tmp3[0]) || isNaN(tmp3[1]) ) {
                        alert('E2_equations1 error: Bad indices in setting array elements: '+fuse);
                        return 0;
                }
                for (var itmp=tmp3[0];itmp<=tmp3[1];itmp++) {
                        aline=tmp2[0]+'['+itmp+']='+tmp1[1];
                        fuseExp=fuseExp.concat(aline);
                }
          }  else {                     //single equation
            fuseExp[0]=fuse;
          }
        } else {
           fuseExp[0]=fuse;                     // not an equation
        }

        for (var itmp=0;itmp<fuseExp.length;itmp++) {
          fuse=fuseExp[itmp] ;
          stuff=E2_parseEqu(fuse);

           ares=E2_doMath(stuff.value,anode) ;          

           if (ares==null) continue ;
           anName=strip(stuff.varname) ;
           if (anName!='' ) {
               doEquationsSet=doEquationsSet+' '+strip(anName) ;     // the list of globals set by a doEquations call
              svy_setVal(anName,ares,anode); 
           } else {
                if (typeof(ares)!='undefined') cumvalue=cumvalue+ares+' ' ;
           }
        }                       // fuseExp DO (expanded v[m:n]
    }                              // funclist2  DO  ( multiple lines in a tring 
}                                      // funclist (array of possibly multi line strings

return cumvalue  ;

}


//=============================== 
// parse equation (x=a+b...) to varname (x) and value (a+b..)
function E2_parseEqu(astring) {
var ac,stuff,aa ;

var oof='<>!';
var stuff=new Object() ;
stuff.varname='' ; stuff.value=astring;
var ieq=astring.indexOf('=');
if (ieq==-1) return stuff ;
if (ieq==0) {
  alert('E2_parseEqu error: nothing before an = ');
  stuff.value='';
  return stuff;
}
// is this = part of a ==, !=, >=, or <=?; or follows a ' or " 
aa=astring.indexOf('"');
if (aa>-1  && aa<ieq) return stuff ;                    // " preceeds =
aa=astring.indexOf("'");
if (aa>-1  && aa<ieq) return stuff ;                    // ' preceeds =

if (astring.substr(ieq+1,1)=='=' ) return stuff  ;                // == is first, this has no varname=
var ac=astring.substr(ieq-1,1);
if (oof.indexOf(ac)!=-1) return stuff ;                         // <=, >= or != is first, this has no varname
stuff.varname=astring.substr(0,ieq);
stuff.value=astring.substr(ieq+1);
return stuff ;

}





//=======================================
// analyze a math string

/* Calculate an equation.
   Syntax: avalue=E2_doMath(equationString,anode)
   or, avalue=E2_doMath(equationObject)
       where equationObject.value contains an "equation string"
   anode is optional. If used, it should be a DOM node (that may contain attributes to be lookedup)

   avalue will be a numeric value, or a string. Or null if an error occurred.

   You can preface values, variables names, etc. with a - sign to indicate "negative value".

   The equation string can contain values, variable names, operators, and functions.
   Parenthesis can be use control order of evaluation.

   The following operators are recognized:
       +  -  *  /  \ ^  == != > < <= >=  & ` |  
   ^ is expoentiation
   \ is modulus division (9%4 = 1)
   & is logical ans
   | is logical or
   ` is logical XOR 
   The ==, !=, <. >, <=, and >=  are logical operators, and return a 0 (false) or 1 (true).
   Note that you can use 2 to 3 letter mnemonics instead of the &, !, `, and the 6 logic operators

   The order of precedence is (with left to right precedence for operators in the same set):
       1) variable names
       2) functions and ( ). Note that arguments are processed recursively, using these rules
       3) ^
       4) * / and \
       5) + and -
       6)  == != > < >= and <=
       7) AND OR  and XOR
       8) $+  (string concatentation).

     Note: instead of == you can use ~ or EQ
                instad of  != you can use ! or NE
                instead of >=  you can use } or GE
                instead of <= you can use {  or LE
                instead of >  you can use  GT
                instead of < you can use  LT
                instead of  &&= you can use & or AND
                instead of ||= you can use | or OR
                instead of XOR you can use `
                instead of $+ you can use $

     Note: when using  mnemonics (such as EQ... XOR) : you MUST have a space before and after, but case does NOT matter.

           Example:  ' myCost >= 100 '
                     ' myCost } 100 '
                     ' myCost Ge  100 '

    You can use scientific notation for your values: i.e.;  1003 or 1.003e3 ,  0.04 or 4.0e-2

    Variable names are either survey-global variables, or values of elements (if preceded by a @).
    Two reserved variables can be used: PI and E (case insensitive).

    Several reserved words are used for logical operations:
        EQ NE GT LT GE LE AND OR XOR
    
    NOT(xx) function returns the logical NOT -- if xx (where xx is any valid equation string) =0, true is returned. Otherwise, false.

    You can include multiple argument functions. For example:
       ' 1.1e3 * ( -34.04 + myFunc2(109/3 , 5^2) ) '

   Also, note that [ and ] are used for "array elements", and @ for "element names".

   You can quote strings (using ' or ").  Quoted strings may contain spaces.
   If you 'quote' a number and use it in normal math calculations, the number will be used.
   If you quote a non-numeric string and use it in normal math calculations, a NaN is returned.

*/

function E2_doMath(athis,Dnode) {

  var avalue,daval,vv,vv0,wastype,ttype,i;


  if (typeof(athis)!=='string') athis=athis.value;
  avalue=strip(athis)+' ' ;                    // add a space to force final processing


//E2_writeStatus(gazebo3+ ' dodmath '+avalue);
  vv0=E2_doMathParse(avalue,-1,Dnode);
//E2_writeStatus(gazebo3+ '  dodmath = '+vv0.value);
  vv=vv0['value'] ;

// check for some errors: adjacent operators,  commas outside of function
wastype='' ;
  for (var i=0;i<vv.length;i++) {
     var ttype=vv[i].type ;
     if (ttype=='COMMA') {
        fatalErrorAlert(avalue,'E2_doMath','comma outside of function: '+avalue);
        return null ;
     }
     if (ttype=='OPER' && wastype=='OPER') {
         fatalErrorAlert(avalue,'E2_doMath','adjacent math operators: '+avalue);
         return null ;
     }
     wastype=ttype ;
   }

// now analyze
  daval=E2_doMathFig(vv,Dnode,avalue,athis) ;

  return daval ;
}
            
// =========================== avalue
// analyze a math equation that has been parsed to an array
function E2_doMathFig(vv,Dnode,avalue,equString) {

var daval,cc,i,t0,i1,dafunc,daval1,vv,aneg,ineg,daval,foo,tmp;
if (arguments.length<4) equString='';


// first, set variables and values
for (var i=0; i<vv.length ; i++) {

//   E2_writeStatus(i+'   eeee '+equString+'='+vv[i].type+','+vv[i].value);

  if (vv[i].type!='VAL'  && vv[i].type!='STRING' ) continue ;

  if (vv[i].type=='VAL') {
    aval=vv[i].value ;
    aneg=vv[i].isneg ;

    var ineg=1 ; if (aneg=='-') ineg=-1 ;
    if (!isNaN(aval)) {
        daval=parseFloat(aval)*ineg;
     } else {
       daval=E2_doMathVar(aval,Dnode,equString) ;
       if (!isNaN(daval) && strip(daval)!=='')  daval=daval*ineg ;
     }
  } else {                      // string
      daval=vv[i].value ;
  }
  vv[i].value=daval ;
}

// now deal with functions
for (var i=0; i<vv.length ; i++) {
  if (vv[i].type!='FUNCTION') continue ;
  aneg=vv[i].isneg ;
  var ineg=1 ; if (aneg=='-') ineg=-1 ;
  dafunc=vv[i].value ;
  newvv=vv[i].fargs ;
  daval=E2_doMathFig(newvv,Dnode,avalue,equString) ;       

  if (!daval.length || typeof(daval)=='string' ) {
       var foo=[] ; foo[0]=daval ; daval=foo ;
  }

  if (dafunc!='')  {                      // not identity
    var dafunc0=dafunc ;
    if (dafunc.indexOf('.')>0) {                   // a Math. function ?
       var zz=dafunc.split('.'); dafunc=zz[0]; dafunc2=zz[1];
       if (dafunc=='MATH') dafunc='Math';
       if (dafunc=='Math') dafunc2=dafunc2.toLowerCase();

       if (!window[dafunc] || !window[dafunc][dafunc2]) {
                alert('Error in function evaluation: no such function= '+dafunc0);
                return 0;
        }
        switch (daval.length) {
           case  0: daval1= window[dafunc][dafunc2](); break
           case  1: daval1= window[dafunc][dafunc2](daval[0]); break
           case  2: daval1=window[dafunc][dafunc2](daval[0],daval[1]); break
           case   3: daval1=window[dafunc][dafunc2](daval[0],daval[1],daval[2]); break
           case   4: daval1=window[dafunc][dafunc2](daval[0],daval[1],daval[2],daval[3]); break
           case   5: daval1=window[dafunc][dafunc2](daval[0],daval[1],daval[2],daval[3],daval[4]); break
           case   6: daval1=window[dafunc][dafunc2](daval[0],daval[1],daval[2],daval[3],daval[4],daval[5]); break
           case   7: daval1=window[dafunc][dafunc2](daval[0],daval[1],daval[2],daval[3],daval[4],daval[5],daval[6]); break
           case   8: daval1=window[dafunc][dafunc2](daval[0],daval[1],daval[2],daval[3],daval[4],daval[5],daval[6],daval[7]); break
           default: alert('E2_doMath error ... too many (more than 8) arguments in function '+ dafunc); return 0 ; break;
       }

    } else if (dafunc.toUpperCase()=='NOT' ) {                       // special case
           daval1= !daval[0];


/* NOT YET IMPLEMENTED    } else if (dafunc.toUpperCase()=='MAKEARRAY' ) {                       // special case
           daval1=getWord(daval[0]);
//alert(daval[0]+' daval1 '+daval1);
*/ 

    } else if (dafunc.toUpperCase()=='IN' ) {                       // special case
           if (daval.length<2) {
              daval1=0;
           } else {
              var needle=daval[0] ;
              var haystack=daval[1];
              var isi=0;
              if (daval.length>2) isi=daval[2] ;
              daval1=Word_inString(needle,haystack,isi);
          }           

    } else if (dafunc.toUpperCase()=='EXIST' || dafunc.toUpperCase()=='EXISTS') {                       // special case
       tmp=parseUsing(equString,'(',')',1);
       daval1= E2_doMathVarExists(tmp[1],Dnode);
//       daval1= E2_doMathVarExists(daval[0],Dnode);

    } else if (dafunc.toUpperCase()=='QUERY' || dafunc.toUpperCase()=='QUERY') {                       // special case
           daval1= Read_Global('$'+daval[0],'');

    } else  {                                    // not a math.ff function, not a special function
        if (typeof(daval)=='string') {
                var tmp=[] ; tmp[0]=daval ; daval=tmp ;
        }

      if (!window[dafunc]) {
          fatalErrorAlert(equString,'E2_doMathFig','no such function ' + dafunc);
          return null ;
        }

       switch (daval.length) {
           case  0: daval1= window[dafunc](); break
           case  1: daval1= window[dafunc](daval[0]); break
           case  2: daval1=window[dafunc](daval[0],daval[1]); break
           case   3: daval1=window[dafunc](daval[0],daval[1],daval[2]); break
           case   4: daval1=window[dafunc](daval[0],daval[1],daval[2],daval[3]); break
           case   5: daval1=window[dafunc](daval[0],daval[1],daval[2],daval[3],daval[4]); break
           case   6: daval1=window[dafunc](daval[0],daval[1],daval[2],daval[3],daval[4],daval[5]); break
           case   7: daval1=window[dafunc](daval[0],daval[1],daval[2],daval[3],daval[4],daval[5],daval[6]); break
           case   8: daval1=window[dafunc](daval[0],daval[1],daval[2],daval[3],daval[4],daval[5],daval[6],daval[7]); break
           default: alert('E2_doMath error: too many ('+daval.length+' is more than 8) arguments in function '+ dafunc);  alert(daval); return 0 ; break;
       }

    }
  } else {                                      // identity
      if (typeof(daval)=='object') {
          daval1=daval[0];
      } else {
         daval1=daval;
      }
  }
  if (!isNaN(daval1) && strip(daval1)!=='') daval1=daval1*ineg ;
  vv[i].value=daval1 ;
  vv[i].type='VAL';

}                               // end of dealing with functions

// find commas
i1=0 ;
cc=[];
for (var i=0; i<vv.length;i++){
  if (vv[i].type=='COMMA') {
     var t0=[] ; t0[0]=i1 ; t0[1]=i;
     i1=i+1;
     var k=cc.length;
     cc[k]=[];
     cc[k]=t0 ;
  }
}
if (i1<vv.length) {
     var t0=[] ; t0[0]=i1 ; t0[1]=vv.length;
     var k=cc.length;
     cc[k]=[];
     cc[k]=t0 ;
}

// for each "argument". do the math. First do ^, then * / \, then + -, then == etc, then AND OR XOR
daval=[];
for (var i=0;i<cc.length ;i++){
  newvv=vv.slice(cc[i][0],cc[i][1]);
  newvv=E2_doMathFig2(newvv,'^',avalue,equString);
  newvv=E2_doMathFig2(newvv,'*/\\',avalue,equString);
  newvv=E2_doMathFig2(newvv,'+-',avalue,equString);
  newvv=E2_doMathFig2(newvv,'<>{}~!',avalue,equString);
  newvv=E2_doMathFig2(newvv,'|&`',avalue,equString);
  newvv=E2_doMathFig2(newvv,'$',avalue,equString);

  daval=daval.concat(newvv[0].value) ;
}
if (daval.length==1) return daval[0];           // just return first element of an array
return daval ;

}

//============= equstr fatal
// check to see if a variable exists
// NOTE: in some cases (i.e.; control panels) this function will be replaced by a more general version
function E2_doMathVarExists(avar,Dnode) {
 var aaname,ac1,ac2,dd,avar2 ;
  if (!isNaN(avar)) return 1 ;                  // numbers always exists
  avar=strip(avar);
   ac1=avar.substr(0,1);
   ac2=avar.substr(avar.length-1);
  if ( (ac1=="'" && ac2=="'") ||   (ac1=='"' && ac2=='"') ) return 1  ; // strings always exist 

// element name exists?
  if (ac1=='@') {
        aname=avar.substr(1);
        var dd=document.getElementsByName(aname);
        if (dd.length==0) return 0 ;
        return 1;
   }

// attribute exists?
  if (ac1=='#') {
        if (anode=='') {
            return 0 ;               // no dom node provided
        }
        var aname=avar.substr(1);
        if (anode.hasAttribute)  {
                dd=anode.hasAttibute(aname) ;
        } else {
           dd=anode.getAttribute(aname);
           if (dd==='' || dd==null) return 0;
        }
        return 1 ;
   }

// survey global (note that wierd stuff, such as EXIST(a+4), should return FALSE
if (ac1=='%')  avar=avar.substr(1);

if (avar.indexOf('.')>-1) {
  avar2=parseAt(avar,'.');
  avar=avar2[0];
  if (avar2[1]=='') {
    dd=Read_priorGlobal('',avar,null);
    if (dd==null) return 0 ;
  } else {
    dd=Read_priorGlobal(avar2[1],avar,null);
    if (dd==null) return 0 ;
  }
} else {
  dd=Read_Global(avar,null);
  if (dd==null) return 0 ;
}

return 1 ;

}

// =====================
// analyze selected operators in array (return collapsed array)
function E2_doMathFig2(vv,dooper,avalue,equString) {
var newv,i,lhs,rhs,aoper,anode,daval,amess;

for (var i=0; i<vv.length;i++) {

 anode=vv[i];

 if (anode.type!='OPER') continue ;

 if (dooper.indexOf(anode.value)==-1) continue ;               // not an operator currently being processed

 if (i==0) {
    fatalErrorAlert(equString,'E2_doMathFig2','no lhs value for oper '+anode.value+ ' ('+avalue ); 
    return null ;                       // this step should NOT be run 
 }
  if (i==vv.length-1) {
    fatalErrorAlert(equString,'E2_doMathFig2',' no rhs value for oper '+anode.value+ ' ('+avalue );
    return null ;
 }

 if (anode.value=='$' || anode.value=='!' || anode.value=='~' || anode.value=='&' || anode.value=='|' ) {                        // string conctaentaion 
     if ( (vv[i-1].type!='VAL' && vv[i-1].type!='STRING') ||  (vv[i+1].type!='VAL' && vv[i+1].type!='STRING') ) {  //can not be conctaentated 
       fatalErrorAlert(equString,'E2_doMathFig2',' lhs or rhs not a string or a value: '+vv[i-1].type + ', '+vv[i+1].type + ' ('+avalue );
       return null ;  
     }
 } else {                                                       // check that both are vals 
   if (vv[i-1].type!='VAL' || vv[i+1].type!='VAL') {
       amess='Math operation ('+anode.value+') error: \n\n ' ;
       if (vv[i-1].type!='VAL') {
           amess=amess+'The LHS is not a VALUE :'+vv[i-1].value+'\n';
        }
       if (vv[i+1].type!='VAL') {
           amess=amess+'The RHS is not a VALUE :'+vv[i+1].value+'\n';
       }
       fatalErrorAlert(equString,'E2_doMathFig2',amess);
       return null ;
    }
 }

 
 daval=E2_doMathCompute(vv[i-1].value,anode.value,vv[i+1].value);
 if (_errorFlag!==0) {
       fatalErrorAlert(equString,'E2_doMathFig2',_errorFlag);
       return null ;
 }

 vv[i+1].type='VAL'; vv[i+1].value=daval ;
 vv[i].type='' ; vv[i-1].type='' ;
 }              // for i
 var newv=[] ;
 for (var i=0; i<vv.length;i++){
   if (vv[i].type!='') newv=newv.concat(vv[i]);
 }

return newv;
}


// =-========================== fatal equs
// parse string to an array
// returns an object (res):
//  res=new Object();
//   res.value=nodelist -- list of "math nodes"
//   res.i=i  -- used in recursive calls

function E2_doMathParse(athis,i,Dnode,stringList) {

var ii,i,operlist,avalue,quoteon,achar,q2,q1,qq,ic,isquote,ic,newstring ;
var isnow,wasoper,nodelist,anew,isneg,achar,res,n2,newstring;

operlist='+-*/\\^{}<>!&|~`$';

if (typeof(athis)!=='string') {                 // will only happen on first call (not on recursive calls)
   avalue=athis.value;
} else {
  avalue=athis;
}

if (arguments.length<2) i=-1;

// do string replacements -- will only happen on first call, not on recursive calls
if (arguments.length<4) {                               // fix up the string 
  avalue=strip(avalue)+' ' ;                    // add a space to force final processing
  newstring='' ;
  ithString=-1;
  stringList=[];
  isquote=0 ;
  for (var ic=0 ; ic<avalue.length;ic++){
    achar=avalue[ic];
    if (achar=="'") {                                   // a '
      switch (isquote) {
        case 0:  isquote=1 ;                            // start a '
                 ithString++ ;                          // move to next free spot 
                 stringList[ithString]='' ;             // initialize
                 newstring=newstring+' "'+ithString+'" ' ;             // "n" is an index
                 continue ;
                 break ;
        case 1 :  isquote=0 ; continue; break;                           // end a '
        case 2:  break ;                              // a ' inside of a " ".. just a normal character 
        default: break ;
      }
    }

   if (achar=='"') {                                   // a "
      switch (isquote) {
         case 0:  isquote=2 ;                            // start a "
                 ithString++ ;                          // move to next free spot 
                 stringList[ithString]='' ;             // initialize
                 newstring=newstring+' "'+ithString+'" ' ;             // "n" is an index
                 continue ;
                 break ;
         case 1 :  break ;                               // " inside of a ', just another char
         case 2:  isquote=0 ; continue ; break ;                   // end a " 
         default: break ;
      }
    }
    if (isquote==0) {
         newstring=newstring+achar ;
    }else {
        stringList[ithString]=stringList[ithString]+achar ;
    }
  }
  avalue=newstring ;

// replace tab and crlf
  avalue=avalue.replace(/\n/g,'  ');  avalue=avalue.replace(/\t/g,' ');

// replace logicals with 1 char equivalents
  avalue=avalue.replace(/\$\+/g,'\$');   avalue=avalue.replace(/==/g,'~');   avalue=avalue.replace(/!=/g,'!');
  avalue=avalue.replace(/>=/g,'}');   avalue=avalue.replace(/<=/g,'{');   avalue=avalue.replace(/&&/g,'&');
  avalue=avalue.replace(/\|\|/g,'\|');

// reserved word replacements
  avalue=avalue.replace(/ or /ig,'\|');   avalue=avalue.replace(/ and /ig,'&');   avalue=avalue.replace(/ eq /ig,'~');
  avalue=avalue.replace(/ ne /ig,'!');   avalue=avalue.replace(/ ge /ig,'}');   avalue=avalue.replace(/ le /ig,'{');
  avalue=avalue.replace(/ gt /ig,'>');   avalue=avalue.replace(/ lt /ig,'<');  avalue=avalue.replace(/ xor /ig,'`');

}      


nodelist=[] ;
isneg='' ;
isnow='' ;

while (1==1) {
  if (i>=avalue.length) break;
  i++;
  achar=avalue[i];

  if (achar=='"') {                       // a quote index -- create a node with the corresponding phrase
      q2=avalue.indexOf('"',i+1); qq=avalue.substring(i+1,q2) ;
     anew=new Object() ;
     anew.type='STRING' ;
     anew.value=stringList[qq] ;
     anew.isneg='' ;
     nodelist=nodelist.concat(anew);
     isnow='' ; isneg=''; wasoper='';
     i=q2 ;
     continue ;
  }


// '(' means start a  function (possibly "identity)"
  if (achar=='(') {                     // start of a function
     n2=E2_doMathParse(avalue,i,Dnode,stringList) ;           //recurse!
     anew=new Object() ;
     anew.type='FUNCTION' ;
     anew.value=isnow ;                 // isnow='' means "identity" function
     anew.isneg=isneg ;
     anew.fargs=n2.value ;
     nodelist=nodelist.concat(anew);
     i=n2.i ;
     isnow='' ; isneg='' ; wasoper='';
     continue ;
   }

   if (achar==')') {                    // end of function. Add current var (if one is being spedified), and return nodelist
       if (isnow!='') {
         anew=new Object() ;
         anew.type='VAL' ;
         anew.value=isnow ;
         anew.isneg=isneg ;
         nodelist=nodelist.concat(anew);
       }
       res=new Object();
       res.value=nodelist;
       res.i=i ;
       return res ;
   }

   if (achar==',') {                            // end of nth argument
       if (isnow!='') {
         anew=new Object() ;
         anew.type='VAL' ;
         anew.value=isnow ;
         anew.isneg=isneg ;
         nodelist=nodelist.concat(anew);
       }
      anew=new Object() ;
      anew.type='COMMA' ;
      nodelist=nodelist.concat(anew);
      isnow='' ; isneg='' ; wasoper='';
      continue;
   }

// ' ' is eithe ignored, or means "end of word".
  if (achar==' ') {                     // space means end of a word. Hence end of number or end of varname
     if (isnow=='' )       continue ;         // ignore extra spaces

// non blank isnow. Create a node with isnow as the value, and add to array

     anew=new Object() ;
     anew.type='VAL' ;
     anew.value=isnow ;
     anew.isneg=isneg ;
     nodelist=nodelist.concat(anew);
     isnow='' ; isneg=''; wasoper='';
     continue ;
   }

// a math operator. Or possibly a + or -

   if (operlist.indexOf(achar)>-1) {
       if ( ( achar=='-' || achar=='+') && isnow!='') {                            // check for sci notation
           if (isnow.substr(isnow.length-1).toUpperCase()=='E') {
              if (isnow.length==1 || !isNaN(isnow.substr(0,isnow.length-2))) {
                  isnow=isnow+achar;
                  continue;
                }
             }
       }

        if (isnow!='' )  {                      // just after a varname or value, so save it
           anew=new Object() ;   anew.type='VAL' ;    anew.value=isnow ;  anew.isneg=isneg ;
           nodelist=nodelist.concat(anew);
           isnow='' ; wasoper=0; isneg='' ;
       }
       if ( achar=='-' || achar=='+') {                            // if following an operator, this is used as a positive or negative ?
          if (wasoper==1 || nodelist.length==0 ) {
            if (isneg!='') {
              fatalErrorAlert(avalue,'E2_doMathParse',' repeated - or + ');
              return null ;
             }
            isneg=achar;
            continue ;
          }
       }                                // save the opeartor
       anew=new Object() ;   anew.type='OPER' ;    anew.value=achar ;
       nodelist=nodelist.concat(anew);
       wasoper=1;
       continue ;
   }

// a character. Add to isnow
    isnow=isnow+achar ;
    wasoper=0 ;

}                       // end of string


 res=new Object();
 res['value']=nodelist;
 res['i']=i ;

//if (athis.indexOf('#')>0) alert(res['value'][0].value+' anewwww231 ' + res['value'][0].type);

 return res ;

}

// ===========================
// do a math/ logical operation, return fvalu
function E2_doMathCompute(oldval,isop,newval) {
var vv, vv0;

// do the math!
_errorFlag=0;                          // _errorFlag is/will be a global

switch (isop) {

 case '$' :                             // string concatenation 
    vv='' + oldval + '' +  newval  ;
    break ;

 case '+' :
    vv=(1*oldval) + (1*newval)  ;
    break ;

 case '-' :
    vv=oldval-newval ;
    break;

 case '*':
    vv=oldval*newval ;
    break ;

 case '\/':
    if (newval==0) {
        _errorFlag='divide by 0 ';
        return null ;
     }
     vv=oldval/newval;
      break ;

  case '\\' :
     if (newval==0) {
        _errorFlag='modulus divide by 0 ';
        return '' ;
     }
     vv=oldval%newval;
     break ;

  case '^':
      vv=Math.pow(oldval,newval);
      break ;

  case '~':
      vv0= oldval===newval;
      vv=0
      if (vv0) vv=1
      break ;

  case '!':
      vv0=oldval!==newval;
      vv=0
      if (vv0) vv=1
      break ;

  case '&':
      vv0=oldval && newval;
      vv=0
      if (vv0) vv=1
      break ;

  case '|':
      vv0=oldval || newval;
      vv=0
      if (vv0) vv=1
      break ;

  case '`':             //xor
      vv0=(oldval && !newval) || (!oldval && newval) 
      vv=0
      if (vv0) vv=1

      break ;

  case '>':
      vv0=oldval>newval;
      vv=0
      if (vv0) vv=1

      break ;

  case '<':
      vv0=oldval<newval;
      vv=0
      if (vv0) vv=1

      break ;

  case '{':
      vv0=oldval<=newval;
      vv=0
      if (vv0) vv=1

      break ;

  case '}':
      vv0=oldval>=newval;
      vv=0
      if (vv0) vv=1

      break ;


   default:
      _errorFlag='no such operator: '+isop;
      return '' ;
      break ;
}
return vv ;

}

// ================
/* lookup a variable. Special cases, that lookup hard coded javascript variables (case sensitive)
E
PI
Otherwise, check for some other conditions, and then call svy_getVal

NOTE: in some cases (i.e.; control panels) this function will be replaced by a more general version
*/

function E2_doMathVar(avar,anode,equString) {


var tavar,zz,aname ;
if (arguments.length<3) equString='';


//if (avar.substr(0,7)=='itick[#') E2_writeStatus(' domatvhar0 of '+avar+','+anode);

  var tavar=avar.toUpperCase() ;
  if (tavar=='E') return Math.E ;
  if (tavar=='PI') return Math.PI ;
  if (avar.replace(/\s/g,'')=="''") {
        zz=strip(avar); 
        zz=zz.substr(1,zz.length-2);
        return zz;
  }

// look for .G, .C, .U, or .R -- these are ERRORS!
    if (tavar.indexOf('.G')>-1 || tavar.indexOf('.C')>-1 || tavar.indexOf('.U')>-1 ) {

           alert('Error: a .x modifier is used in a variable in an equation: '+equString); return 0;
   }

 if (avar.substr(0,1)=='#') {            // attribute 
   if (typeof(anode)!='object' || !anode.getAttribute ) {
        if (debugOn()) E2_writeStatus('Warning: attribute ('+avar+') lookup, but no node. empty string will be used ');
        return '' ;                     // no attriburtes, return ''
   }
   var aname=avar.substr(1);
   if (aname=='') aname='IDX';
   if (aname.toUpperCase()=='VALUE') {
        zz=anode.value ;
   } else {
      zz=anode.getAttribute(aname) ;
   }
   return zz;
 }

 if (avar.substr(0,1)=='@') {            // value of an element. If @ by itself, of current element (if one is available)
    var aname=avar.substr(1);
    if (aname=='') {                      // value of current element  (might be value attribute, might be innerhtml
       if (typeof(anode)!='object') {
         if (debugOn()) E2_writeStatus('Warning: element value ('+avar+') lookup, but no node. empty string will be used ');
         return '' ;                     // no attriburtes, return ''
       }
       zz=get_elemValue(anode,'');
       return zz ;
    }                            // current element 
    zz=svy_getVal(avar,'',anode);
    return zz ;
  }        

  var zz=svy_getVal(avar,'',anode);                      // either a survey global, or a prior

  return zz ;
}



//===================
// display message, and force an abort
function fatalErrorAlert(aequ,awhere,amess) {
var vv,origequ;
origequ='';
if (typeof(_originalEquation)!=='undefined') {
   vv=confirm('Error (in ' +awhere +'): '+amess+'\n Equation:'+aequ+'\n Original equation: '+_originalEquation+'\n Abort? ');
} else {
   vv=confirm('Error (in ' +awhere +'): '+amess+'\n Equation:'+aequ+'\n\n Abort? ');
}
if (vv) javascriptStopNow() ;
return 0;
}

