var jeq_error="";
var jeq_mathfunctions=/^(sin|cos|tan|asin|acos|atan|abs|floor|ceil|exp|sqrt)$/i;
var jeq_newfunctions=/^(erf|sec|csc|cot|sech|csch|coth|sinh|cosh|tanh|asinh|acosh|atanh|asec|acsc|acot|asech|acsch|acoth|ln|log|u)$/i;

function jeq_evalx(eq,x) {
  return eval(jeq(eq));
}
function jeq_sec(x) { return 1/Math.cos(x); }
function jeq_csc(x) { return 1/Math.sin(x); }
function jeq_cot(x) { return 1/Math.tan(x); }
function jeq_asec(x) { return Math.acos(1/x); }
function jeq_acsc(x) { return Math.asin(1/x); }
function jeq_acot(x) { return Math.atan(1/x); }
function jeq_ln(x) { return Math.log(x); }
function jeq_log(x) { return Math.log(x)/Math.log(10); }
function jeq_sinh(x) { return (Math.exp(x)-Math.exp(-x))/2; }
function jeq_cosh(x) { return (Math.exp(x)+Math.exp(-x))/2; }
function jeq_tanh(x) { return (Math.exp(x)-Math.exp(-x))/(Math.exp(x)+Math.exp(-x)); }
function jeq_asinh(x) { return Math.log(x+Math.sqrt(x*x+1)); }
function jeq_acosh(x) { return Math.log(x+Math.sqrt(x*x-1)); }
function jeq_atanh(x) { return 0.5*Math.log((1+x)/(1-x)); }
function jeq_sech(x) { return 2/(Math.exp(x)+Math.exp(-x)); }
function jeq_csch(x) { return 2/(Math.exp(x)-Math.exp(-x)); }
function jeq_coth(x) { return (Math.exp(x)+Math.exp(-x))/(Math.exp(x)-Math.exp(-x)); }
function jeq_asech(x) { return Math.log(1/x+Math.sqrt(1/x/x-1)); }
function jeq_acsch(x) { return Math.log(1/x+Math.sqrt(1/x/x+1)); }
function jeq_acoth(x) { return 0.5*Math.log((1+x)/(1-x)); }
function jeq_u(x) { return (x>=0); }

function haselement(v,e) {for(var i=0;i<v.length;i++) if(v[i]==e) return true;return false;}

function fixpowers(v) {
  if(v==null) { jeq_error?null:jeq_error="syntax error"; return null; }
  for(i=0;i<v.length;i++) {
    if(isArray(v[i])) { v[i]=fixpowers(v[i]); if(v[i]==null) { jeq_error?null:jeq_error="syntax error"; return null; } }
  }
  for(var i=0;i<v.length;i++) {
    if(v[i]=='^') {
      if(v[i-1]==null||v[i+1]==null) { jeq_error="^ requires two arguments, for example x^2 or (x+1)^(x+2)."; return null; }
      v.splice(i-1,3,new Array('Math.pow',new Array('(',v[i-1],',',v[i+1],')')));
      i-=2;
    }
  }
  return v;
}
function fixfunctions(v) {
  if(v==null) {jeq_error?null:jeq_error="syntax error"; return null;}
  for(i=0;i<v.length;i++) {
    if(isArray(v[i])) {
      v[i]=fixfunctions(v[i]);
      if(v[i]==null) {jeq_error?null:jeq_error="syntax error"; return null;} 
    }
  }
  for(var i=0;i<v.length;i++) {
    if(!isArray(v[i])) {
      if(v[i].match(jeq_mathfunctions)) {
        if(v[i+1]==null) {jeq_error="function "+v[i]+ " requires an argument."; return null;}
        v[i]='Math.'+v[i].toLowerCase();
        v.splice(i,2,new Array('(',v[i],v[i+1],')'));
        i--;
      } else if(!(v[i]==null) && v[i].match(jeq_newfunctions)) {
        if(v[i+1]==null) {jeq_error="function "+v[i]+ " requires an argument."; return null;}
        v[i]='jeq_'+v[i].toLowerCase();
        v.splice(i,2,new Array('(',v[i],v[i+1],')'));
        i--;
      }
    }
  }
  return v;
}

function jeq(eq,vars) {
  var tokens;
  var e,i;
  var pstart=-1,pend;
  jeq_error="";
  if(vars==""||vars==null) vars="x";
  if(vars=="-") vars="__NONE__";
  e=eq.replace(/ /g,"");
  e=e.replace(/([0-9])([a-df-z]|[a-z][a-z]|\()/ig,"$1*$2");
  e=e.replace(/(\))([0-9a-df-z]|[a-z][a-z]|\()/ig,"$1*$2");
  e=e.replace(/([a-z0-9\.])([^a-z0-9\.])/ig,"$1 $2");
  e=e.replace(/([^a-z0-9\.])([a-z0-9\.])/ig,"$1 $2");
  e=e.replace(/(\-|\)|\()/g," $1 ");
  tokens=e.split(/ +/);
  for(i=0;i<tokens.length;i++) {
    tokens[i]=tokens[i].replace(/ /g,"");
    tokens[i]=tokens[i].replace(/_/g,".");
    if(tokens[i]=='') { tokens.splice(i,1);i--; }
    else if(tokens[i]=='pi') { tokens[i]='3.14159265358979'; }
    else if(tokens[i]=='e') { tokens[i]='2.718281828459045'; }
    else if(tokens[i].length>0 && tokens[i].match(/^[a-z][a-z0-9]*$/i) && !tokens[i].match(new RegExp("^"+vars+"$","i")) && !tokens[i].match(jeq_mathfunctions) && !tokens[i].match(jeq_newfunctions)) { jeq_error="invalid variable or function: "+tokens[i]; jeq_doerror("error in expression "+eq+":\n"+jeq_error);return null; }
  }
  while(haselement(tokens,'(')||haselement(tokens,')')) {
    pstart=-1;
    for(i=0;i<tokens.length;i++) {
      if(tokens[i]=='(') pstart=i;
      if(tokens[i]==')'&&pstart==-1) { jeq_error="unmatched right parenthesis )"; jeq_doerror("error in equation "+eq+":\n"+jeq_error); return null; }
      if(tokens[i]==')'&&pstart!=-1) {
        tokens.splice(pstart,i-pstart+1,tokens.slice(pstart,i+1));
        i=-1;
        pstart=-1;
      }
    }
    if(pstart!=-1) { jeq_error="unmatched left parenthesis (";jeq_doerror("error in equation "+eq+":\n"+jeq_error);return null; }
  }
  tokens=fixfunctions(tokens);
  tokens=fixpowers(tokens);
  if(tokens==null) { jeq_doerror("error in equation "+eq+":\\n"+jeq_error); return null; }
  return joinarray(tokens);
}

function jeq_doerror(e) {
  alert(e);
}

function isArray(v) {if(v==null){return 0;}if(v.constructor.toString().indexOf("Array")==-1)return false;return true;}

function joinarray(v) {var t="";for(var i=0;i<v.length;i++){if(isArray(v[i])){ t+=joinarray(v[i]);}else{t+=v[i];}}return t;}


